上一篇我们了解了render过程中调用的createElement方法和VNode虚拟节点。createElement对_createElement进行了封装,而_createElement则是真正处理render过程的函数,VNode类则定义了许多虚拟节点需要的字段,VNode构成节点树,再映射成真实DOM。那么接下来我们来了解VNode渲染成真实DOM的过程。
当我们创建Vue实例,在data里添加一些初始参数后,界面渲染的时候会调用_update方法。我们首先找到src/core/instance/lifecycle.js
文件,可以看到lifecycleMixin方法,该方法中有_update
、$forceUpdate
、$destroy
三个方法。先看_update方法:
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
_update
调用了__patch__
方法,__patch__方法有许多环境的判断,比如app和web,服务端和非服务端。这里我们讲app端。我们构建实例的时候:
var app = new Vue({
el: '#app',
render:function(createElement){
return createElement('div',{
attrs:{
id:'app'
}
},this.message)
},
data: {
message: 'Hello Vue!'
}
})
message值Hello Vue!会被渲染在id为app的div元素文本,此时触发_update。vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
,参数vm.$el对应id为app的DOM元素,vnode是之前render函数的返回值,hydrating在非服务端渲染的情况下为false,removeOnly为false。
接下来看__patch__函数的执行关键过程(patch.js):
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node
patchVnode(oldVn