什么时候调用patch
在beforeMount和mounted之间,会执行options.render函数生成新的VNode树。然后调用vm._update(新VNode)更新,然后进入到patch阶段。
// src/core/instance/lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
callHook(vm, 'beforeMount')
let updateComponent = () => {
vm._update(vm._render(), hydrating)
}
vm._watcher = new Watcher(vm, updateComponent, noop)
hydrating = false //默认不是ssr渲染
callHook(vm, 'mounted')
}
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
const prevEl = vm.$el
const prevVnode = vm._vnode
const prevActiveInstance = activeInstance
activeInstance = vm
vm._vnode = vnode
if (!prevVnode) {
// 旧虚拟节点不存在,第一次渲染,hydrating=false表示不是ssr
vm.$el = vm.__patch__(
vm.$el, vnode, hydrating, false /* removeOnly */,
vm.$options._parentElm,
vm.$options._refElm
)
vm.$options._parentElm = vm.$options._refElm = null
} else {
// 不是第一次渲染, patch对比新旧虚拟dom树
vm.$el = vm.__patch__(prevVnode, vnode)
}
activeInstance = prevActiveInstance
// 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
}
}
在src/core/vdom/patch.js中
从createPatchFunction() => patch() =>patchVnode() => updateChildren()。
updateChildren()里面就是diff双端对比算法了
// vue2 使用双端比较算法,借鉴于开源项目:snabbdom,但最早采用双端比较算法的库是 citojs
return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {
if (isUndef(vnode)) {
// 不存在新节点则直接销毁旧节点
if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
// 第一次挂载不存在旧节点,直接渲染新节点
isInitialPatch = true
createElm(vnode, insertedVnodeQueue, parentElm, refElm)
} else {
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElem