const hooksToMerge = Object.keys(componentVNodeHooks)
function installComponentHooks (data: VNodeData) {
const hooks = data.hook || (data.hook = {})
for (let i = 0; i < hooksToMerge.length; i++) {
const key = hooksToMerge[i]
const existing = hooks[key]
const toMerge = componentVNodeHooks[key]
if (existing !== toMerge && !(existing && existing._merged)) {
hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
}
}
}
function mergeHook (f1: any, f2: any): Function {
const merged = (a, b) => {
f1(a, b)
f2(a, b)
}
merged._merged = true
return merged
}
在数据对象上设置hook对象,从data.hook对象中获取key对应的已存在的方法。合并用户传递的hook方法和框架自带的hook方法,合并的过程就是分别执行两个方法。
componentVNodeHooks
componentVNodeHooks对象上存放了init,prepatch,insert和destroy钩子函数。
const componentVNodeHooks = {
init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
......
},
prepatch(oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
......
},
insert (vnode: MountedComponentVNode) {
......
},
destroy (vnode: MountedComponentVNode) {
......
}
}
init
init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
if (
vnode.componentInstance &&
!vnode.componentInstance._isDestroyed &&
vnode.data.keepAlive
) {
const mountedNode: any = vnode
componentVNodeHooks.prepatch(mountedNode, mountedNode)
} else {
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
)
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
}
}
如果组件是被keep-alive包裹的组件,那就调用prepatch钩子函数,传入的新旧VNode参数都是同一个VNode。否则调用createComponentInstanceForVnode创建组件实例,将组件实例赋值给vnode的componentInstance属性。然后执行组件的$mount方法,进入挂载阶段,接下来就是通过编译器得到render函数,接着走挂载、patch这条路,直到组件渲染到页面。
createComponentInstanceForVnode
function createComponentInstanceForVnode (
vnode: any,
parent: any,
): Component {
const options: InternalComponentOptions = {
_isComponent: true,
_parentVnode: vnode,
parent
}
const inlineTemplate = vnode.data.inlineTemplate
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render
options.staticRenderFns = inlineTemplate.staticRenderFns
}
return new vnode.componentOptions.Ctor(options)
}
配置好options配置项,_isComponent表示当前实例是组件实例,_parentVnode表示当前的vnode,parent表示当前激活的组件实例。检查内联模版渲染函数,获取render和staticRenderFns。
然后new创建实例:这里的vnode.componentOptions.Ctor对应的是组件的构造函数,new的时候会调用组件实例的_init方法。
prepatch
prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
const options = vnode.componentOptions
const child = vnode.componentInstance = oldVnode.componentInstance
updateChildComponent(
child,
options.propsData,
options.listeners,
vnode,
options.children
)
},
获取新VNode的配置,获取老VNode的组件实例,然后用新的VNode配置更新旧的VNode上的各种属性。
insert
insert (vnode: MountedComponentVNode) {
const { context, componentInstance } = vnode
if (!componentInstance._isMounted) {
componentInstance._isMounted = true
callHook(componentInstance, 'mounted')
}
if (vnode.data.keepAlive) {
if (context._isMounted) {
queueActivatedComponent(componentInstance)
} else {
activateChildComponent(componentInstance, true /* direct */)
}
}
},
如果组件未挂载,则调用mounted声明周期钩子,然后处理处理 keep-alive 组件的异常情况。
destroy
destroy (vnode: MountedComponentVNode) {
const { componentInstance } = vnode
if (!componentInstance._isDestroyed) {
if (!vnode.data.keepAlive) {
componentInstance.$destroy()
} else {
deactivateChildComponent(componentInstance, true /* direct */)
}
}
}
如果组件实例没有被销毁并且没有被keepAlive组件包裹,就调用$destroy。如果被keepalive组件包裹的话就调用deactivated钩子函数,负责让组件失活,不销毁组件实例,从而缓存组件的状态。