1、前言
Vue.js框架是目前比较火的MVVM框架之一,简单易上手的学习曲线,友好的官方文档,配套的构建工具,让Vue.js在2016大放异彩,大有赶超React之势。前不久Vue.js 2.0正式版已出,在体积优化(相比1.0减小了50%)、性能提高(相比1.0提高60%)、API优化等各方面都更上一层楼;前端
本文是系列文章,主要想经过对于Vue.js 2.0源码的分析,从代码层面解析Vue.js的实现原理,帮助读者可以更深刻地理解整个框架的思想。此篇文章主要介绍前端渲染部分;node
不足之处还请批评指正,欢迎一块儿交流学习。web
2、Vue的初始化
咱们在使用Vue.js的时候,最基本的一个使用,就是在HTML引入Vue.js的库文件,并写以下一段代码:算法
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
new Vue,本质就是生成一个Vue的对象,咱们来了解一下这个生成Vue对象的过程是怎样的:数据结构
首先,Vue的入口是/src/entries/web-runtime-with-compiler.js
,这是由config.js
配置文件决定的。app
这个入口文件中import了不少文件,其中有一条主要的脉络:框架
/src/entries/web-runtime-with-compiler.js
引用了/src/entries/web-runtime.js
引用了/src/core/index.js
引用了/src/core/instance/index.js
dom
其中/src/core/instance/index.js是最核心的初始化代码,其中:函数
红框部分,就是整个Vue的类的核心方法。其含义给读者解读一下:工具
//初始化的入口,各类初始化工做 initMixin(Vue) //数据绑定的核心方法,包括经常使用的$watch方法 stateMixin(Vue) //事件的核心方法,包括经常使用的$on,$off,$emit方法 eventsMixin(Vue) //生命周期的核心方法 lifecycleMixin(Vue) //渲染的核心方法,用来生成render函数以及VNode renderMixin(Vue)
其中new Vue就是执行下面的这个函数:
_init
方法就是initMixin
中的_init
方法。
至此,程序沿着这个_init方法继续走下去。
3、Vue的渲染逻辑——Render函数
在定义完成Vue对象的初始化工做以后,本文主要是讲渲染部分,那么咱们接上面的逻辑,看Vue.js是如何渲染页面的。在上图中咱们看到有一个initRender
的方法:
在该方法中会执行红框部分的内容:
而$mount方法就是整个渲染过程的起始点。具体定义是在/src/entries/web-runtime-with-compiler.js中,根据代码整理成流程图:
由此图能够看到,在渲染过程当中,提供了三种渲染模式,自定义Render函数、template、el都可以渲染页面,也就是对应咱们使用Vue时,三种写法:
自定义Render函数
Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, // tag name 标签名称 this.$slots.default // 子组件中的阵列 ) }, props: { level: { type: Number, required: true } } })
template写法
var vm = new Vue({
data: { // 以一个空值声明 `msg` msg: '' }, template: '<div>{{msg}}</div>'
})
el写法(这个就是入门时最基本的写法)
var app = new Vue({
el: '#app', data: { message: 'Hello Vue!' }
})
这三种渲染模式最终都是要获得Render函数。只不过用户自定义的Render函数省去了程序分析的过程,等同于处理过的Render函数,而普通的template或者el只是字符串,须要解析成AST,再将AST转化为Render函数。
记住一点,不管哪一种方法,都要获得Render函数。
咱们在使用过程当中具体要使用哪一种调用方式,要根据具体的需求来。
若是是比较简单的逻辑,使用template和el比较好,由于这两种都属于声明式渲染,对用户理解比较容易,但灵活性比较差,由于最终生成的Render函数是由程序经过AST解析优化获得的;
而使用自定义Render函数至关于人已经将逻辑翻译给程序,可以胜任复杂的逻辑,灵活性高,但对于用户的理解相对差点。
4、Vue的渲染逻辑——VNode对象&patch方法
根据上面的结论,咱们不管怎么渲染,最终会获得Render函数,而Render函数的做用是什么呢?咱们看到在/src/core/instance/lifecycle.js中有这么一段代码:
vm._watcher = new Watcher(vm, () => { vm._update(vm._render(), hydrating) }, 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
有多种实现方式,但基本思路都是同样的,分为两步:
在Vue.js 2.0中Javascript模拟DOM模型树就是VNode,Render函数执行后都会返回VNode对象,为下一步操做作准备。在/src/core/vdom/vnode.js中,咱们能够看到VNode的具体数据结构:
VNode的数据结构中还有VNodeData、VNodeDirective、VNodeComponentOptions,这些数据结构都是对DOM节点的一些描述,本文不一一介绍。读者能够根据源码来理解这些数据结构。(PS:Vue.js使用了flow,标识了参数的静态类型,对理解代码颇有帮助^_^)
咱们知道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树。
5、Vue的渲染小结
回过头来看,这里的渲染逻辑并非特别复杂,核心关键的几步流程仍是很是清晰的:
new Vue,执行初始化
挂载$mount
方法,经过自定义Render
方法、template、el
等生成Render
函数
经过Watcher
监听数据的变化
当数据发生变化时,Render
函数执行生成VNode
对象
经过patch
方法,对比新旧VNode
对象,经过DOM Diff
算法,添加、修改、删除真正的DOM元素
做者:Generon
来源:CSDN
原文:https://blog.csdn.net/generon... 版权声明:本文为博主原创文章,转载请附上博文连接!