new Vue()做了什么Vue挂载的实现- render函数
- Virtoal DOM
- createElement 函数
- update 函数
当前分析Vue的版本是
2.6.10
new Vue()做了什么
首先,Vue是一个构造函数,通过new关键字是用来实例化一个对象。
- 源码在
src/core/instance/index.js
上图可以看到Vue函数内部会调用this._init(options)方法,options就是我们在new Vue({...})中传入的参数。而this._init方法定义在src/core/instance/init.js文件中:
从源码中我们可以看出初始化主要做了以下事情:
- 合并配置
- 初始化生命周期
initLifecycle(vm) - 初始化事件中心
initEvents(vm) - 初始化渲染
initRender(vm) - 初始化
data、props、computed、watcher等。 - 初始化的最后,如果检测到
options传入了el属性,则调用vm.$mount()方法挂载vm,挂载的目标就是把模板渲染成最终的DOM。
Vue 挂载的实现
Vue中我们是通过$mount实例方法去挂载的,因为$mount方法是和平台、构建方式相关的。我们重点分析带compiler版本的$mount实现。
- $mount的实现在
src/platforms/web/entry-runtime-with-compiler.js中:
- 这段代码首先缓存了原型上的$mount方法。然后对el做了限制,Vue不能挂载在body、html这样的根节点上。
- 如果没有定义render方法,则会把el或者template属性,最终都会转化为render方法。(Vue组件的渲染最终都需要render方法,无论是.vue、还是el、或者template属性)
- 原型上的$mount方法是在
src/platforms/web/runtime/index.js中:
$mount方法传入两个参数,第一个参数el,表示挂载的元素,也可以是字符串,也可以是DOM对象,如果是字符串会调用query方法转化为DOM对象,第二个参数是服务端渲染相关的。$mount方法最后会调用mountComponent方法。
mountComponent方法定义在src/core/instance/lifecycle.js中:
从上面代码可以看出,mountComponent核心就是先调用vm.render()方法先生成虚拟Node,再实例化一个渲染Watcher,在它的回调函数中会调用updateComponent方法,最终调用vm._update()更新DOM。
函数的最后判断根节点设置了vm._isMounted = true来表示这个实例已经挂载了,同时执行mounted钩子函数。
这里注意vm.$vnode表示Vue实例的父虚拟Node,所以为Null表示当前是根Vue的实例。
render
我们平常开发中写的较多的是template模板,很少直接手写render方法,在之前的mounted方法实现中,会把template编译成render方法。
template写法:
<div id="app">
{{message}}
</div>
render函数写法:
render: function(createElement){
return createElement('div', {
attrs: {
id: 'app'
}
}, this.message)
}
接下来我们看下_render方法的定义:
- _render方法定义在src/core/instance/render.js中
_render方法用来把实例渲染成一个虚拟Node。
从vnode = render.call(vm._renderProxy, vm.$createElement)可以看出render函数中的createElement就是vm.$createElement方法
vm.$createElement方法在定义initRender方法的时候已经执行了,除了vm.$createElement方法,还有一个个vm._c方法,它是被模板编译成render函数使用。两个方法内部都调用了createElement方法。
Virtual DOM
DOM操作代价大,