Vue渲染过程浅析


Posted in Javascript onMarch 14, 2019

Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。但是模板毕竟是模板,不是真实的dom节点。从模板到真实dom节点还需要经过一些步骤

  1. 把模板编译为render函数
  2. 实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom
  3. 对比虚拟dom,渲染到真实dom
  4. 组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3

第一步: 模板到render

在我们使用Vue的组件化进行开发应用的时候, 如果仔细的查看我们要引入的组件, 例子如下

// App.vue 
<template>
  <div>
    hello word
  </div>
</template>

<script>

export default {
}

</script>

<style>

</style>

在我们的主入口main.js

import Vue from 'vue'
import App from './App'

console.log(App)

new Vue({
 render: h => h(App)
}).$mount('#app')

Vue渲染过程浅析

我们能够看到在我们引入的App这个模块,里面是一个对象,对象里面存在一个方法叫做render。在说render函数之前,我们可以想一想,每一次加载一个组件,然后对模板进行解析,解析完后,生成Dom,挂载到页面上。这样会导致效率很低效。而使用Vue-cli进行组件化开发,在我们引入组件的后,其实会有一个解析器(vue-loader)对此模板进行了解析,生成了render函数。当然,如果没有通过解析器解析为render函数,也没有关系,在组件第一次挂载的时候,Vue会自己进行解析。源码请参考: https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-runtime-with-compiler.js

这样,能保证组件每次调用的都是render函数,使用render函数生成VNode。

第二步:虚拟节点VNode

我们把Vue的实例挂载到#app, 会调用实例里面的render方法,生成虚拟DOM。来看看什么是虚拟节点,把例子修改一下。

new Vue({
 render: h => {
  let root = h(App)
  console.log('root:', root)
  return root
 }
}).$mount('#app')

Vue渲染过程浅析

上面生成的VNode就是虚拟节点,虚拟节点里面有一个属性elm, 这个属性指向真实的DOM节点。因为VNode指向了真实的DOM节点,那么虚拟节点经过对比后,生成的DOM节点就可以直接进行替换。

这样有什么好处呢?

一个组件对象,如果内部的data发生变化,触发了render函数,重新生成了VNode节点。那么就可以直接找到所对应的节点,然后直接替换。那么这个过程只会在本组件内发生,不会影响其他的组件。于是组件与组件是隔离的。
例子如下:

// main.js
const root = new Vue({
 data: {
  state: true
 },
 mounted() {
  setTimeout(() => {
   console.log(this)
   this.state = false
  }, 1000)
 },
 render: function(h) {
  const { state } = this // state 变化重新触发render
  let root = h(App)
  console.log('root:', root)
  return root
 }
}).$mount('#app')
// App.vue
<script>
export default {
 render: (h) => {
  let app = h('h1', ['hello world'])
  console.log('app:', app)
  return app
 }
}
</script>

Vue渲染过程浅析

我们可以看到,当main.js中重新触发render函数的时候,render方法里面有引用App.vue这个子组件。但是并没有触发App.vue组件的的render函数。

在一个组件内,什么情况会触发render?

如何才能触发组件的render

数据劫持是Vue的一大特色,原理官方已经讲的很多了深入响应式原理。在我们给组件的data的属性进行的赋值的时候(set),此属性如果在组件内部初次渲染过程被引用(data的属性被访问,也就是数据劫持的get), 包括生命周期方法或者render方法。于是会触发组件的update(beforeUpdate -> render -> updated)。

注: 为了防止data被多次set从而触发多次update, Vue把update存放到异步队列中。这样就能保证多次data的set只会触发一次update。

当props会触发组件的重新渲染是怎么发生的呢?

把父组件的data通过props传递给子组件的时候,子组件在初次渲染的时候生命周期或者render方法,有调用data相关的props的属性, 这样子组件也被添加到父组件的data的相关属性依赖中,这样父组件的data在set的时候,就相当于触发自身和子组件的update。

例子如下:

// main.vue
import Vue from 'vue'
import App from './App'

const root = new Vue({
 data: {
  state: false
 },
 mounted() {
  setTimeout(() => {
   this.state = true
  }, 1000)
 },
 render: function(h) {
  const { state } = this // state 变化重新触发render
  let root = h(App, { props: { status: state } })
  console.log('root:', root)
  return root
 }
}).$mount('#app')

window.root = root
// App.vue
<script>
export default {
 props: {
  status: Boolean
 },
 render: function (h){
  const { status } = this
  let app = h('h1', ['hello world'])
  console.log('app:', app)
  return app
 }
}
</script>

截图如下:

Vue渲染过程浅析

main.js中 state 状态发生了变化,由false => true, 触发了自身与子组件的render方法。

补充

上面的内容是本人的一些使用心得,由于水平有限, 内容有些错误或者表达不当。多欢迎大神来指导!!!

PS:vue渲染过程的{{xxx}}显示的解决办法

这是由于浏览器的渲染机制导致的,浏览器是从头到尾  如果你的js引用在底部,那么浏览器会先加载dom此时,你用于渲染的{{}}识别符,因为还没读到该识别符对应的js文件,所以会被解析为字符串而显示在页面中,我们可以用过自定义属性v-cloak解决,

实例对象对应标签中加入 v-cloak:

<div id="wrap" v-cloak>

然后在css中给定义属性选择器 

[v-cloak]{


display:none

}

vue实例创建完成后会把v-cloak去掉,在没创建实例对象时,该标签内的内容都会被隐藏

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Jquery乱码的一次解决过程 图解教程
Feb 20 Javascript
Jquery 最近浏览过的商品的功能实现代码
May 14 Javascript
基于jQuery的Spin Button自定义文本框数值自增或自减
Jul 17 Javascript
关于jquery动态增减控件的一些想法和小插件
Aug 01 Javascript
javascript学习笔记(八) js内置对象
Jun 19 Javascript
原生JavaScript实现滚动条效果
Mar 24 Javascript
微信小程序 出现47001 data format error原因解决办法
Mar 10 Javascript
JavaScript实现离开页面前提示功能【附jQuery实现方法】
Sep 26 jQuery
JavaScript中构造函数与原型链之间的关系详解
Feb 25 Javascript
jQuery实现input[type=file]多图预览上传删除等功能
Aug 02 jQuery
vue 使用 canvas 实现手写电子签名
Mar 06 Javascript
vue调用微信JSDK 扫一扫,相册等需要注意的事项
Jan 03 Vue.js
详解关于JSON.parse()和JSON.stringify()的性能小测试
Mar 14 #Javascript
详解使用React制作一个模态框
Mar 14 #Javascript
JavaScript碎片—函数闭包(模拟面向对象)
Mar 13 #Javascript
详解js动态获取浏览器或页面等容器的宽高
Mar 13 #Javascript
详解jQuery-each()方法
Mar 13 #jQuery
详解使用Nuxt.js快速搭建服务端渲染(SSR)应用
Mar 13 #Javascript
react同构实践之实现自己的同构模板
Mar 13 #Javascript
You might like
PHP 和 MySQL 开发的 8 个技巧
2007/01/02 PHP
调整优化您的LAMP应用程序的5种简单方法
2011/06/26 PHP
PHP生成短网址的3种方法代码实例
2014/07/08 PHP
PHP使用内置dir类实现目录遍历删除
2015/03/31 PHP
PHP读取配置文件类实例(可读取ini,yaml,xml等)
2015/07/28 PHP
thinkphp5 + ajax 使用formdata提交数据(包括文件上传) 后台返回json完整实例
2020/03/02 PHP
javascript支持firefox,ie7页面布局拖拽效果代码
2007/12/20 Javascript
JQuery datepicker 使用方法
2011/05/20 Javascript
jquery重新播放css动画所遇问题解决
2013/08/21 Javascript
基于jQuery实现表单提交验证
2014/11/24 Javascript
JS实现自动阅读单词(有道单词本添加功能)
2016/11/14 Javascript
详解JavaScript中js对象与JSON格式字符串的相互转换
2017/02/14 Javascript
如何解决vue与传统jquery插件冲突
2017/03/20 Javascript
Vue.js与 ASP.NET Core 服务端渲染功能整合
2017/11/16 Javascript
vue toggle做一个点击切换class(实例讲解)
2018/03/13 Javascript
elementUI中Table表格问题的解决方法
2018/12/04 Javascript
使用vue-router切换页面时,获取上一页url以及当前页面url的方法
2019/05/06 Javascript
Vue-resource安装过程及使用方法解析
2020/07/21 Javascript
vue3.0自定义指令(drectives)知识点总结
2020/12/27 Vue.js
python实现键盘控制鼠标移动
2020/11/27 Python
Python用类实现扑克牌发牌的示例代码
2020/06/01 Python
python 爬取百度文库并下载(免费文章限定)
2020/12/04 Python
使paramiko库执行命令时在给定的时间强制退出功能的实现
2021/03/03 Python
HTML5的结构和语义(3):语义性的块级元素
2008/10/17 HTML / CSS
video下autoplay属性无效的解决方法(添加muted属性)
2020/05/19 HTML / CSS
男女时尚与复古风格在线购物:RoseGal(全球免费送货)
2017/07/19 全球购物
Kidsroom台湾:来自德国的婴儿用品
2017/12/11 全球购物
教师自荐信范文
2013/12/09 职场文书
优秀体育委员自荐书
2014/01/31 职场文书
我的梦想演讲稿
2014/04/30 职场文书
高三毕业典礼演讲稿
2014/05/13 职场文书
2014年政协委员工作总结
2014/12/01 职场文书
小学教师年度个人总结
2015/02/05 职场文书
python 实现定时任务的四种方式
2021/04/01 Python
Node-Red实现MySQL数据库连接的方法
2021/08/07 MySQL
SpringBoot系列之MongoDB Aggregations用法详解
2022/02/12 MongoDB