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 相关文章推荐
jqGrid增加时--判断开始日期与结束日期(实例解析)
Nov 08 Javascript
js简单的弹出框有关闭按钮
May 05 Javascript
JQuery中两个ul标签的li互相移动实现方法
May 18 Javascript
跟我学习javascript的垃圾回收机制与内存管理
Nov 23 Javascript
利用jQuery实现WordPress中@的ID悬浮显示评论内容
Dec 11 Javascript
基于javascript实现动态时钟效果
Aug 18 Javascript
关于JavaScript和jQuery的类型判断详解
Oct 08 Javascript
简单实现jQuery级联菜单
Jan 09 Javascript
Bootstrap table表格简单操作
Feb 07 Javascript
Angular如何引入第三方库的方法详解
Jul 13 Javascript
vue之a-table中实现清空选中的数据
Nov 07 Javascript
何时/使用 Vue3 render 函数的教程详解
Jul 25 Javascript
详解关于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中文分词的简单实现代码分享
2011/07/17 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(四)
2014/06/23 PHP
CodeIgniter针对数据库的连接、配置及使用方法
2016/03/03 PHP
Symfony2框架创建项目与模板设置实例详解
2016/03/17 PHP
JS中剪贴板兼容性、判断复制成功或失败
2021/03/09 Javascript
使弱类型的语言JavaScript变强势
2009/06/22 Javascript
js 与 php 通过json数据进行通讯示例
2014/03/26 Javascript
jquery实现实时改变网页字体大小、字体背景色和颜色的方法
2015/08/05 Javascript
JS实现带鼠标效果的头像及文章列表代码
2015/09/27 Javascript
javascript弹出窗口实现代码
2015/11/12 Javascript
js表单中选择框值的获取及表单的序列化
2015/12/17 Javascript
通过sails和阿里大于实现短信验证
2017/01/04 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
JS简单实现父子窗口传值功能示例【未使用iframe框架】
2017/09/20 Javascript
详解使用路由延迟加载 Angular 模块
2017/10/12 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
2019/02/15 jQuery
如何在JavaScript中谨慎使用代码注释
2019/06/21 Javascript
Vue+scss白天和夜间模式切换功能的实现方法
2021/01/05 Vue.js
[26:52]LGD vs EG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python实现把utf-8格式的文件转换成gbk格式的文件
2015/01/22 Python
python获得linux下所有挂载点(mount points)的方法
2015/04/29 Python
Python中定时任务框架APScheduler的快速入门指南
2017/07/06 Python
梯度下降法介绍及利用Python实现的方法示例
2017/07/12 Python
Python获取当前公网ip并自动断开宽带连接实例代码
2018/01/12 Python
Python代码缩进和测试模块示例详解
2018/05/07 Python
使用NumPy和pandas对CSV文件进行写操作的实例
2018/06/14 Python
python实现坦克大战游戏 附详细注释
2020/03/27 Python
python requests库爬取豆瓣电视剧数据并保存到本地详解
2019/08/10 Python
tensorflow实现从.ckpt文件中读取任意变量
2020/05/26 Python
python爬虫爬取淘宝商品比价(附淘宝反爬虫机制解决小办法)
2020/12/03 Python
Joe Fresh官网:加拿大时尚品牌和零售连锁店
2016/11/30 全球购物
大专生自荐信
2013/10/04 职场文书
团党委领导干部党的群众路线教育实践活动个人对照检查材料思想汇
2014/10/05 职场文书
司法局2014法制宣传日活动总结
2014/11/01 职场文书
遗失说明具结保证书
2015/02/26 职场文书
社区公民道德宣传日活动总结
2015/03/23 职场文书