引言

相信绝大多数的前端小伙伴已记不清做了多少项目,写了多少代码了,每个人如同教科书般地写着Vue代码:

// 单文件组件中常见代码
export default {data () {return {msg: 'click me'}},methods: {say () {this.msg = 'well done'}}
}// 入口文件中的常见代码
new Vue({el: '#app',router: router,render: h => h(App)
})

一切都显得那么自然。不过在百忙之中是否有小伙伴想过,一个小小的Vue实例怎么有这么大的能量,竟然可以构建出如此复杂的前端项目。那么Vue内部是如何运转的呢,做了哪些事情呢,从今天开始跟着我一探究竟。

vue是可以运行在多平台上的如浏览器,weex等,本文只分析vue在浏览器环境下的主线执行流程。

初始化

我们先看一下Vue的构造函数:

// Vue构造函数
function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}// 执行初始化逻辑this._init(options)
}

从Vue的构造函数中可以看到,当我们执行new Vue()的时候,只执行了一个_init方法。_init会根据传入的选项对vue进行初始化。props、data、生命周期,事件机制的初始化都是在此过程中完成的。

以data的初始化为例,vue会通过 Object.defineProperty 的方式将data的属性定义到vue实例上。这也就解释了为什么我们可以在vue中通过对 this.msg 进行赋值,可以修改data中属性的值了。

以上对data的处理只是刚刚开始。为了能实现所谓的响应式或者数据驱动更新,vue又做了进一步的处理,具体做法是,创建一个observer对象,该对象与data绑定,通过 Object.defineProperty 将data中的所有的属性转换成getter/setter。当data中的属性在vue实例中被访问(会触发getter),observer 对象就会把该属性收集为watcher实例的依赖,之后当data中的属性在vue实例中被改变(会触发setter), observer 会通知依赖该属性的 watcher 实例重新渲染页面。

注:每个watcher都对应一个vue实例

以上处理流程串在一起,vue就实现了通过修改 this.msg 从而触发页面的自动更新。

最后借用vue官网上的一张示意图帮助大家再理解下这个处理过程:

模板解析

通过上面的分析,我们已经知道当数据发生变化时,会触发页面的重新渲染。接下来我们分析下vue是如何进行渲染的。

首先,vue会把将我们编写的HTML模板解析成一个AST描述对象,该对象是通过childrenparent链接而成的树形结构,完整地描述了HTML标签的所有信息。

例如有如下HTML模板:

<div id="app"><p>{{msg}}</p>
</div>

最终会解析成如下形式的AST对象:

{attrs: [{name: "id", value: ""app"", dynamic: undefined, start: 5, end: 13}],attrsList: [{name: "id", value: "app", start: 5, end: 13}],attrsMap: {id: "app"},children: [{attrsList: [],attrsMap: {},children: [],end: 33,parent: {type: 1, tag: "div", ...},plain: true,pre: undefined,rawAttrsMap:{},start: 19tag: "p",type: 1}],end: 263,parent: undefined,plain: false,rawAttrsMap:{id: {name: "id", value: "app", start: 5, end: 13}},start: 0tag: "div",type: 1
}

然后 vue 根据AST对象生成 render 函数,该函数的函数体大致如下:

with(this){return _c('div', {attrs:{"id":"app"}}, [_c('p', [_v(_s(msg))])])
}

也就是说,我们的模板最终在vue内部都是会以一个render函数的形式存在。

vue官网上对此也有提及,一般推荐大家使用template,el等方式来指定模板,此外还可以通过使用render来自定义个性化的编译函数,不过vue内部最终都会解析成render函数。

先虚后实

我们得到render函数之后,vue并未直接渲染成DOM树,而是先通过render函数得到一个vnode。实际上这一步是非常有必要的,我们都知道频繁大量地操作DOM节点是极耗性能的。vue在渲染之前通过对vnode的比较,可以大大规避非必要的DOM操作。下面是一个vnode大致结构:

{tag: "div",children: [{tag: "p", ...}],data: {attrs: {id: "app"}}elm: DOM节点(div#app),parent: undefined,context: Vue实例,...
}

最后,vue根据diff之后的结果,执行真正的dom节点的插入更新删除等操作,同时触发vue实例的生命周期钩子函数。之后,vue要做的就是观察数据的变化,进而决定是否重新渲染页面了。

总结

以上就是vue在初始渲染过程中的主干流程,大体总结起来就是先对选项对象初始化,通过Object.defineProperty建立一套响应式系统,然后将模板解析成render函数,然后使用render函数生成vnode,在渲染前,对vnode进行diff操作,最后进行必要的渲染。

本文并未深入每个执行中的代码细节,接下来会详细对初始化、响应式实现原理、模板渲染、指令解析、vnode的diff等进行介绍,敬请期待。

推荐阅读

  • 如何使用 splitChunks 精细控制代码分割
  • 用了这么久的react/vue,你真的了解hooks吗?

vue data 值如何渲染_Vue执行流程解析相关推荐

  1. vue data 值如何渲染_vue源码阅读复盘-watcher模块

    回顾目标 数据驱动视图: 理解watcher.dep.observer这三个对象之间的关系 这和VUE对象又有什么关系? 这和视图又有什么关系? 叙述过程 先彻底理解了一下VUE的简介,并写出了一份建 ...

  2. SpringMVC 执行流程解析

    SpringMVC 执行流程解析 注:SpringMVC 版本 5.2.15 上面这张图许多人都看过,本文试图从源码的角度带大家分析一下该过程. 1. ContextLoaderListener 首先 ...

  3. Postgresql源码(66)insert on conflict语法介绍与内核执行流程解析

    相关: <Postgresql源码(66)insert on conflict语法介绍与内核执行流程解析>) <Postgresql源码(70)逻辑复制DecodeXLOG主要流程和 ...

  4. mybatis mysql 调用存储过程 多个返回值_MyBatis的SQL执行流程不清楚?很简单!

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 作者:双子孤狼- blog.csdn.net/zwx900102/article/details/108455514 ...

  5. 使用Caffe进行手写数字识别执行流程解析

    之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下 ...

  6. Caffe中对MNIST执行train操作执行流程解析

    之前在 http://blog.csdn.net/fengbingchun/article/details/49849225 中简单介绍过使用Caffe train MNIST的文章,当时只是仿照ca ...

  7. vue add element报错_Vue 源码解析 -- new Vue -gt; mountComponent 001

    这一系列文章的学习出处于Vue.js 技术揭秘 | Vue.js 技术揭秘,有兴趣的伙伴自行阅览 本文涉及到vue源码文件中的 src/core/instance/index.js ==> ne ...

  8. springsecurity原理执行流程_3. Spark原理-执行流程解析

    概念 Job 当在RDD上执行Action操作时就会提交一个Job.这个Job会将RDD分为多分,并将每份RDD提交到集群执行. Stage 当一个Job提交后将分解为多个Stage. Task 一个 ...

  9. openGauss简单查询SQL的执行流程解析

    目录 简单查询的执行 gdb调试 上一期酷哥分析了openGauss数据库的启动过程,包括主线程,辅助线程及业务处理线程的启动过程,这一期主要分析简单查询语句在业务处理线程Postgres上的执行流程 ...

最新文章

  1. 通过VNC Viewer使用VMware虚拟机的远程桌面连接
  2. python人脸识别毕业设计-Python基于Dlib的人脸识别系统的实现
  3. 芙蓉之约 , Serverless 技术实践营 3.25 成都站邀你来参加
  4. 高斯混合模型(Gaussian Mixture Model)
  5. EasyExcel 导出时 Converter转换器 注入 ExcelContentProperty 为null
  6. 初识C++之函数重载
  7. 软件工程学习总结(2)——大部分的软件工程其实就是管道作业
  8. 遇到事情不计较就是善待自己
  9. 每天Leetcode 刷题 初级算法篇-杨辉三角
  10. 华为云计算HCNA--存储虚拟化
  11. vue对象属性为null_如何避免在Vue中使用null作为class的空值
  12. python下载文件的三种方法
  13. 实践四 -- 文本词频分析
  14. Java setlocale方法_Java Configuration.setLocale方法代碼示例
  15. 低深度测序下的拷贝数变异文章阅读笔记
  16. 自己搭建服务器提供IOS IPA包下载
  17. 【jQuery】- form 表单 reset()方法不生效解决
  18. 企业微信中打开的小程序wx.qy.getEnterpriseUserInfo接口调用报错42013
  19. GraphicsLab Project之HDR渲染
  20. JavaScript获取屏幕高度和宽度等信息

热门文章

  1. 外媒称Windows 10是一辆“广告大巴车”
  2. L(A/N)MP 中遇到的MySQL的坑
  3. Ubuntu和Win7双系统,ubuntu被删,重新启动之后显示,no such partition
  4. 多个网站tracert测试用的批处理
  5. Dns服务器与iptables的问题【已解决】
  6. t430装Linux双系统,thinkpad T430s 在win8上安装ubuntu双系统(UEFI开启)
  7. 达摩java_JAVA面向对象
  8. 面试官问:什么是布隆过滤器?
  9. linux fedora安装simplescreenrecorder
  10. linux centos7开启IP转发、路由转发解决docker 端口映射 及外部无法访问问题