vue核心面试题:vue父子组件生命周期调用顺序

tech2023-09-04  90

一、顺序

1.加载渲染过程

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted

2.子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

3.父组件更新过程

父beforeUpdate->父updated

4.销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

二、理解

组件的调用顺序都是先父后子,渲染完成的顺序肯定是先子后父

组件的销毁操作是先父后子,销毁完成的顺序是先子后父

三、原理

当dom渲染时,会createElm创建元素,创建元素后会进行初始化,初始化组件的时候内部还有组件,会不停的去渲染,所以它的渲染顺序是先父后子,完成的顺序是先子后父。

dom渲染描述:先父组件要创建beforeCreate、created,父组件实例化完成后要挂载这个父组件beforeMount,挂载父组件的时候会调用父的render方法,渲染的时候发现里面有子组件,这时就会调用子组件的beforeCreate、created、beforeMount,当子组件都完成之后,会把子组件先存起来,这儿有队列,不是子完成就会调用子的mounted,因为子组件中可能还有子组件,这时会暂存一下,到最后子全完成了会按照子调父的调,mounted是先子后父。

源码:在dom渲染和更新时就会调用patch方法,在patch方法中有一个insertedVnodeQueue数组,会将所有的vnode存放在insertedVnodeQueue中,最后会在整个创建完之后会调用invokeInsertHook方法,依次调用收集的insert hook。在patch中会调用createElm方法来创建元素,createElm方法中会对元素进行判断:如果元素是组件,会调用createComponent方法创建组件,调用组件的init方法渲染当前组件的内容,通过initComponent方法将pendingInsert插入到自己的insertedVnodeQueue中,然后将它置为null,最后会调用invokeCreateHooks方法将vnode存放在insertedVnodeQueue中;如果元素不是组件,会调用createChildren方法递归遍历子节点(子组件),createChildren方法中会再次调用createElm方法,直到元素不是一个组件时。

当所有组件都完成后,会调用invokeInsertHook方法,里面会循环通过insert方法依次调用收集的hook,在insert方法中就会触发mounted。

 

四、源码:

1.patch

function patch (oldVnode, vnode, hydrating, removeOnly) { if (isUndef(vnode)) { if (isDef(oldVnode)) invokeDestroyHook(oldVnode) return } let isInitialPatch = false const insertedVnodeQueue = [] // 定义收集所有组件的insert hook方法的数组 if (isUndef(oldVnode)) { isInitialPatch = true createElm(vnode, insertedVnodeQueue) // 创建元素 } // 最终会依次调用收集的insert hook invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) return vnode.elm }

2.createElm

function createElm ( vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index ) { //判断元素是否是组件,如果是执行完createComponent方法放回true就return不执行下面,如果不是组件,继续执行调用createChildren方法 if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { return } // createChildren会递归创建儿子组件 createChildren(vnode, children, insertedVnodeQueue) })

3.createComponent(用来创建组件)

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) { let i = vnode.data if (isDef(i)) { const isReactivated = isDef(vnode.componentInstance) && i.keepAlive if (isDef(i = i.hook) && isDef(i = i.init)) { i(vnode, false /* hydrating */) } if (isDef(vnode.componentInstance)) { initComponent(vnode, insertedVnodeQueue) // 调用组件的init方法,渲染当前组件的内容 insert(parentElm, vnode.elm, refElm) if (isTrue(isReactivated)) { reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm) } return true } } }

4.initComponent 

// 将pendinginsert插入到自己的queue中 function initComponent (vnode, insertedVnodeQueue) { if (isDef(vnode.data.pendingInsert)) { insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert) vnode.data.pendingInsert = null } vnode.elm = vnode.componentInstance.$el if (isPatchable(vnode)) { invokeCreateHooks(vnode, insertedVnodeQueue) setScope(vnode) } else { registerRef(vnode) // make sure to invoke the insert hook insertedVnodeQueue.push(vnode) } }

5.invokeCreateHooks

// 将组件的vnode插入到数组中 function invokeCreateHooks (vnode, insertedVnodeQueue) { for (let i = 0; i < cbs.create.length; ++i) { cbs.create[i](emptyNode, vnode) } i = vnode.data.hook // Reuse variable if (isDef(i)) { if (isDef(i.create)) i.create(emptyNode, vnode) if (isDef(i.insert)) insertedVnodeQueue.push(vnode) } }

6.invokeInsertHook 

// 最后所以组件完成后调用,依次调用收集的insert hook function invokeInsertHook (vnode, queue, initial) { // delay insert hooks for component root nodes, invoke them after the // element is really inserted if (isTrue(initial) && isDef(vnode.parent)) { vnode.parent.data.pendingInsert = queue } else { for (let i = 0; i < queue.length; ++i) { queue[i].data.hook.insert(queue[i]) // 调用insert方法 } } }

7.insert 

// insert方法中会依次调用mounted方法 function insert (vnode) { var context = vnode.context; var componentInstance = vnode.componentInstance; if (!componentInstance._isMounted) { componentInstance._isMounted = true; callHook(componentInstance, 'mounted'); // 调用mounted } if (vnode.data.keepAlive) { if (context._isMounted) { queueActivatedComponent(componentInstance); } else { activateChildComponent(componentInstance, true /* direct */); } } }

8.createChildren 

// 递归遍历子节点,然后再次调用createElm方法 function createChildren (vnode, children, insertedVnodeQueue) { if (Array.isArray(children)) { if (process.env.NODE_ENV !== 'production') { checkDuplicateKeys(children) } for (let i = 0; i < children.length; ++i) { createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i) } } else if (isPrimitive(vnode.text)) { nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text))) } }

9.销毁

Vue.prototype.$destroy = function () { callHook(vm, 'beforeDestroy') // vm.__patch__(vm._vnode, null) // 先销毁儿子 callHook(vm, 'destroyed') }

 

最新回复(0)