Vuejs 2.0源码解析之渲染篇

Posted 小时光茶社

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vuejs 2.0源码解析之渲染篇相关的知识,希望对你有一定的参考价值。

王鹤,腾讯前端高级工程师,参与过QQ情侣、QQ星影联盟、QQ个性化装扮等项目的研发。秉承「不想当产品经理的程序员,不是好的设计师」。除敲敲代码外,对产品、设计、摄影也有一定的兴趣。欢迎一起交流,谈理(CHUI)想(SHUI),聊人(BA)生(GUA)~


1前言


1、Vuejs框架是目前比较火的MVVM框架之一,简单易上手的学习曲线,友好的官方文档,配套的构建工具,让Vuejs在2016大放异彩,大有赶超react之势。前不久Vuejs 2.0正式版已出,优化体积(相比1.0减少了50%),提升性能(相比1.0提升60%),API优化等,各方面更上一层楼。


2、本文是系列文章,主要想通过对于Vuejs 2.0源码的分析,从代码层面解析Vuejs的实现原理,帮助读者能够更深入地理解整个框架的思想。此篇文章主要介绍前端渲染部分。


3、不足之处还请批评指正,欢迎一起交流学习。


2Vue的初始化


我们在使用Vuejs的时候,最基本的一个使用,就是在html引入Vuejs的库文件,并写如下一段代码:




new Vue,本质就是生成一个Vue的对象,我们来了解一下这个生成Vue对象的过程是怎样的。


1、首先,vue的入口是/src/entries/web-runtime-with-compiler.js,这是由config.js配置文件决定的。


Vuejs 2.0源码解析之渲染篇


这个入口文件中import了很多文件,其中有一条主要的脉络:


Vuejs 2.0源码解析之渲染篇


其中/src/core/instance/index.js是最核心的初始化代码,其中:


Vuejs 2.0源码解析之渲染篇


红框部分,就是整个Vue的类的核心方法。其含义给读者解读一下:


Vuejs 2.0源码解析之渲染篇


其中new Vue就是执行下面的这个函数:


Vuejs 2.0源码解析之渲染篇


_init方法就是initMixin中的_init方法


Vuejs 2.0源码解析之渲染篇


至此,程序沿着这个_init方法继续走下去。


3Vue的渲染逻辑—render函数


在定义完成Vue对象的初始化工作之后,本文主要是讲渲染部分,那么我们接上面的逻辑,看Vuejs是如何渲染页面的。在上图中我们看到有一个initRender的方法:


Vuejs 2.0源码解析之渲染篇


在该方法中会执行红框部分的内容:


Vuejs 2.0源码解析之渲染篇


$mount方法就是整个渲染过程的起始点。具体定义是在/src/entries/web-runtime-with-compiler.js中,根据代码整理成流程图:


Vuejs 2.0源码解析之渲染篇


由此图可以看到,在渲染过程中,提供了三种渲染模式,自定义render函数、template、el均可以渲染页面,也就是对应我们使用vue时,三种写法:


1、自定义render函数


Vuejs 2.0源码解析之渲染篇


2、template写法


Vuejs 2.0源码解析之渲染篇


3、el写法(这个就是入门时最基本的写法)


Vuejs 2.0源码解析之渲染篇


这三种渲染模式最终都是要得到render函数。只不过用户自定义的render函数省去了程序分析的过程,等同于处理过的render函数,而普通的template或者el只是字符串,需要解析成AST,再将AST转化为render函数。


记住一点,无论哪种方法,都要得到render函数。


我们在使用过程中具体要使用哪种调用方式,要根据具体的需求来。


1、如果是比较简单的逻辑,使用template和el比较好,因为这两种都属于声明式渲染,对用户理解比较容易,但灵活性比较差,因为最终生成的render函数是由程序通过AST解析优化得到的。


2、而使用自定义render函数相当于人已经将逻辑翻译给程序,能够胜任复杂的逻辑,灵活性高,但对于用户的理解相对差点。


4Vue的渲染逻辑—VNode对象&patch方法


根据上面的结论,我们无论怎么渲染,最终会得到render函数,而render函数的作用是什么呢?我们看到在/src/core/instance/lifecycle.js中有这么一段代码:


Vuejs 2.0源码解析之渲染篇


意思就是,通过Watcher的绑定,每当数据发生变化时,执行_update的方法,此时会先执行vm._render(),在这个vm._render()中,我们的render函数会执行,而得到VNode对象。




VNode对象是个什么东西?VNode就是Vuejs 2.0中的Virtual DOM,在Vuejs 2.0中,相较Vuejs 1.0引入了Virtual DOM的概念,这也是Vuejs 2.0性能提升的一大关键。Virtual DOM有多种实现方式,但基本思路都是一样的,分为两步:


1、javascript模拟DOM模型树


在Vuejs 2.0中Javascript模拟DOM模型树就是VNode,render函数执行后都会返回VNode对象,为下一步操作做准备。在/src/core/vdom/vnode.js中,我们可以看到VNode的具体数据结构:




VNode的数据结构中还有VNodeData, VNodeDirective, VNodeComponentOptions,这些数据结构都是对DOM节点的一些描述,本文不一一介绍。读者可以根据源码来理解这些数据结构。(PS:Vuejs使用了flow,标识了参数的静态类型,对理解代码很有帮助^_^)


2、DOM模型树通过DOM Diff算法查找差异,将差异转为真正DOM节点


我们知道render函数执行生成了VNode,而VNode只是Virtual DOM,我们还需要通过DOM Diff之后,来生成真正的DOM节点。在Vuejs 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树。


5Vue的渲染小结


回过头来看,这里的渲染逻辑并不是特别复杂,核心关键的几步流程还是非常清晰的:


1、new Vue,执行初始化


2、挂载$mount方法,通过自定义render方法、template、el等生成render函数


3、通过watcher监听数据的变化,当数据发生变化时


4、render函数执行生成VNode对象


5、通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素


至此,整个new Vue的渲染过程完毕。

以上是关于Vuejs 2.0源码解析之渲染篇的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs571- Vue 虚拟DOM和Diff算法源码解析

MVVM源码解析之模板解析篇

MVVM源码解析之模板解析篇

Spring 源码解析之HandlerAdapter源码解析

Spring 源码解析之HandlerAdapter源码解析

Spring 源码解析之HandlerAdapter源码解析