Vue.js 2.0源码解析之前端渲染篇
2017-04-02 00:29
686 查看
一、前言
Vue.js框架是目前比较火的MVVM框架之一,简单易上手的学习曲线,友好的官方文档,配套的构建工具,让Vue.js在2016大放异彩,大有赶超React之势。前不久Vue.js 2.0正式版已出,在体积优化(相比1.0减少了50%)、性能提升(相比1.0提升60%)、API优化等各方面都更上一层楼。本文是系列文章,主要想通过对于Vue.js 2.0源码的分析,从代码层面解析Vue.js的实现原理,帮助读者能够更深入地理解整个框架的思想。此篇文章主要介绍前端渲染部分。
不足之处还请批评指正,欢迎一起交流学习。
二、Vue的初始化
我们在使用Vue.js的时候,最基本的一个使用,就是在HTML引入Vue.js的库文件,并写如下一段代码:1.var app = new Vue({ 2. el: '#app', 3. data: { 4. message: 'Hello Vue!' 5. } 6.})
new Vue,本质就是生成一个Vue的对象,我们来了解一下这个生成Vue对象的过程是怎样的:
首先,Vue的入口是/src/entries/web-runtime-with-compiler.js,这是由config.js配置文件决定的。
这个入口文件中import了很多文件,其中有一条主要的脉络:
/src/entries/web-runtime-with-compiler.js
引用了
/src/entries/web-runtime.js
引用了
/src/core/index.js
引用了
/src/core/instance/index.js
其中
/src/core/instance/index.js是最核心的初始化代码,其中:
红框部分,就是整个Vue的类的核心方法。其含义给读者解读一下:
1.//初始化的入口,各种初始化工作 2.initMixin(Vue) 3.//数据绑定的核心方法,包括常用的$watch方法 4.stateMixin(Vue) 5.//事件的核心方法,包括常用的$on,$off,$emit方法 6.eventsMixin(Vue) 7.//生命周期的核心方法 8.lifecycleMixin(Vue) 9.//渲染的核心方法,用来生成render函数以及VNode 10.renderMixin(Vue)
其中new Vue就是执行下面的这个函数:
_init方法就是initMixin中的
_init方法。
至此,程序沿着这个
_init方法继续走下去。
三、Vue的渲染逻辑——Render函数
在定义完成Vue对象的初始化工作之后,本文主要是讲渲染部分,那么我们接上面的逻辑,看Vue.js是如何渲染页面的。在上图中我们看到有一个initRender的方法:
在该方法中会执行红框部分的内容:
而
$mount方法就是整个渲染过程的起始点。具体定义是在
/src/entries/web-runtime-with-compiler.js中,根据代码整理成流程图:
由此图可以看到,在渲染过程中,提供了三种渲染模式,自定义Render函数、template、el均可以渲染页面,也就是对应我们使用Vue时,三种写法:
1. 自定义Render函数
1.Vue.component('anchored-heading', { 2. render: function (createElement) { 3. return createElement( 4. 'h' + this.level, // tag name 标签名称 5. this.$slots.default // 子组件中的阵列 6. ) 7. }, 8. props: { 9. level: { 10. type: Number, 11. required: true 12. } 13. } 14.})
2.template写法
1.var vm = new Vue({ 2. data: { 3. // 以一个空值声明 `msg` 4. msg: '' 5. }, 6. template: '<div>{{msg}}</div>' 7.})
3.el写法(这个就是入门时最基本的写法)
1.var app = new Vue({ 2. el: '#app', 3. data: { 4. message: 'Hello Vue!' 5. } 6.})
这三种渲染模式最终都是要得到Render函数。只不过用户自定义的Render函数省去了程序分析的过程,等同于处理过的Render函数,而普通的template或者el只是字符串,需要解析成AST,再将AST转化为Render函数。
记住一点,无论哪种方法,都要得到Render函数。
我们在使用过程中具体要使用哪种调用方式,要根据具体的需求来。
如果是比较简单的逻辑,使用template和el比较好,因为这两种都属于声明式渲染,对用户理解比较容易,但灵活性比较差,因为最终生成的Render函数是由程序通过AST解析优化得到的;
而使用自定义Render函数相当于人已经将逻辑翻译给程序,能够胜任复杂的逻辑,灵活性高,但对于用户的理解相对差点。
四、Vue的渲染逻辑——VNode对象&patch方法
根据上面的结论,我们无论怎么渲染,最终会得到Render函数,而Render函数的作用是什么呢?我们看到在/src/core/instance/lifecycle.js中有这么一段代码:
1.vm._watcher = new Watcher(vm, () => { 2. vm._update(vm._render(), hydrating) 3.}, noop);
意思就是,通过Watcher的绑定,每当数据发生变化时,执行
_update的方法,此时会先执行
vm._render(),在这个
vm._render()中,我们的Render函数会执行,而得到VNode对象。
VNode对象是什么?VNode就是Vue.js 2.0中的Virtual DOM,在Vue.js 2.0中,相较Vue.js 1.0引入了Virtual DOM的概念,这也是Vue.js 2.0性能提升的一大关键。Virtual DOM有多种实现方式,但基本思路都是一样的,分为两步:
1. Javascript模拟DOM模型树
在Vue.js 2.0中Javascript模拟DOM模型树就是VNode,Render函数执行后都会返回VNode对象,为下一步操作做准备。在
/src/core/vdom/vnode.js中,我们可以看到VNode的具体数据结构:
VNode的数据结构中还有VNodeData、VNodeDirective、VNodeComponentOptions,这些数据结构都是对DOM节点的一些描述,本文不一一介绍。读者可以根据源码来理解这些数据结构。(PS:Vue.js使用了flow,标识了参数的静态类型,对理解代码很有帮助^_^)
2. DOM模型树通过DOM Diff算法查找差异,将差异转为真正DOM节点
我们知道Render函数执行生成了VNode,而VNode只是Virtual DOM,我们还需要通过DOM Diff之后,来生成真正的DOM节点。在Vue.js 2.0中,是通过
/src/core/vdom/patch.js中的
patch(oldVnode, vnode ,hydrating)方法来完成的。
该方法有三个参数oldVnode表示旧VNode,vnode表示新VNode,hydrating表示是否直接使用服务端渲染的DOM元素,这个本文不作讨论,在服务端渲染篇再详细介绍。
其主要逻辑为当VNode为真实元素或旧的VNode和新的VNode完全相同时,直接调用createElm方法生成真实的DOM树,当VNode新旧存在差异时,则调用patchVnode方法,通过比较新旧VNode节点,根据不同的状态对DOM做合理的添加、删除、修改DOM(这里的Diff算法有兴趣的读者可以自行阅读patchVnode方法,鉴于篇幅不再赘述),再调用createElm生成真实的DOM树。
五、Vue的渲染小结
回过头来看,这里的渲染逻辑并不是特别复杂,核心关键的几步流程还是非常清晰的:new Vue,执行初始化
挂载
$mount方法,通过自定义Render方法、template、el等生成Render函数
通过Watcher监听数据的变化
当数据发生变化时,Render函数执行生成VNode对象
通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素
至此,整个new Vue的渲染过程完毕。
相关文章推荐
- 使用v-html解决Vue.js渲染过程中html标签不能被解析(html标签显示为字符串)
- Vue.js源码解析(八)【Vue.js异步更新DOM策略及nextTick】
- Vue.js高仿饿了么外卖App 前端框架Vue.js 1.0升级2.0
- Vue.js 2.0 和 React、Augular等其他前端框架大比拼
- Vue.js解析(三)【从Vue.js源码角度再看数据绑定】
- 前端框架Vue(17)——基于 Vue.js 的服务端渲染 (ssr) 通用应用框架 Nuxt.js
- 使用v-html解决Vue.js渲染过程中html标签不能被解析(html标签显示为字符串)
- Vue.js源码解析(七)【聊聊Vue.js的template编译】
- Vue.js 源码学习七 —— template 解析过程学习
- Vue.js 源码学习八 —— HTML解析细节学习
- Vue.js源码学习四 —— 渲染 Render 初始化过程学习
- Vue.js源码解析(九)【从template到DOM(Vue.js源码角度看内部运行机制)】
- Vue源码学习之初始化模块init.js解析
- 用v-html解决Vue.js渲染中html标签不被解析的问题
- apache mina2.0源码解析(一)
- PureMVC(JS版)源码解析(六):MacroCommand类
- PureMVC(JS版)源码解析(三):Observer类
- PureMVC(JS版)源码解析(十):Controller类
- JS动态修改页面EasyUI datebox不生效、EasyUI动态添加Class、EasyUI动态渲染解析解决方案
- Web前端面试题 Js把URL中的参数解析为一个对象