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

一、顺序

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')
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值