1、父子组件生命周期
和dom相关的都是先子组件;
- 加载渲染过程
父beforeCreate->父created->父beforeMount->
子beforeCreate->子created->子beforeMount->
子mounted->
父mounted
- 子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程
父beforeUpdate->父updated
- 销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
2、源码解析
子组件注册
- 全局组件放到Vue构造函数的options上,子组件实例化会合并该options
- 最终都会放到组件实例vm.$options.components上
createComponent
- render阶段createElement 生成vnode
- 解析节点tag,如果为vm.$options.components的属性 或者 不是string,则会执行 vnode= createComponent()
- 构造子类构造函数 :Ctor=baseCtor.extend(Ctor)即执行Vue.entend
- 安装组件钩子函数:包括init、prepatch、insert、destory,分别是初次渲染、更新、activated激活、销毁的逻辑
- 返回一个占位 vnode
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return
}
const baseCtor = context.$options._base
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
resolveConstructorOptions(Ctor)
// extract props
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
const listeners = data.on
data.on = data.nativeOn
const slot = data.slot
installComponentHooks(data)
// 返回placeholder vnode
const name = Ctor.options.name || tag
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
return vnode
}
patch
patch 的过程会调用 createElm 创建元素节点,接着执行createComponent,最终执行子组件init钩子函数
function createElm (
vnode,
insertedVnodeQueue,
parentElm,
refElm,
nested,
ownerArray,
index
) {
// ...
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
return
}
// ...
}
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
// 执行init钩子函数
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
// vnode 为组件,则留出placeholder插入
if (isDef(vnode.componentInstance)) {
initComponent(vnode, insertedVnodeQueue)
insert(parentElm, vnode.elm, refElm)
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}
- init逻辑如下,vnode.componentOptions.Ctor 对应的就是子组件的构造函数,运行Vue.prototype._init方法来合并配置、初始化数据状态
init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
)
child.$mount(vnode.elm)
},
export function createComponentInstanceForVnode (
vnode: any, // we know it's MountedComponentVNode but flow doesn't
parent: any, // activeInstance in lifecycle state
): Component {
const options: InternalComponentOptions = {
_isComponent: true,
_parentVnode: vnode,
parent
}
return new vnode.componentOptions.Ctor(options)
}
- child.$mount(undefined, false),它最终会调用 mountComponent 方法,
- 进而执行子组件 vm._render() 最终生成vnode;执行完 vm._render 生成 VNode 后,接下来就要执行 vm._update ,又会继续调用patch方法
- 在完成组件的整个 patch 过程后,最后执行 insert(parentElm, vnode.elm, refElm) 完成组件的 DOM 插入,如果组件 patch 过程中又创建了子组件,那么DOM 的插入顺序是先子后父。
欢迎关注我的前端自检清单,我和你一起成长