vue core interview question: vue parent-child component life cycle call sequence

1, Order

1. Load render pass

Parent beforecreate - > parent created - > parent beforemount - > child beforecreate - > child created - > child beforemount - > child mounted - > parent mounted

2. Sub component update process

Parent BeforeUpdate - > child BeforeUpdate - > child updated - > parent updated

3. Parent component update process

Parent BeforeUpdate - > parent updated

4. Destruction process

Parent beforedestroy - > child beforedestroy - > child destroyed - > parent destroyed

2, Understand

The calling order of components is from parent to child, and the order of rendering must be from child to parent

The destruction operation of components is from parent to child, and the order of destruction is from child to parent

3, Principle

When dom rendering, createElm will create elements. After creating elements, initialization will be carried out. When initializing components, there are components inside, which will be rendered continuously. Therefore, its rendering order is from parent to child, and the order of completion is from child to parent.

dom rendering Description: the previous parent component needs to create beforeCreate and created. After the parent component is instantiated, mount the parent component beforeMount. When the parent component is mounted, the parent render method will be called. When it is found that there are child components in the rendering, the beforeCreate, created and beforeMount of the child components will be called. When the child components are completed, the child components will be stored first. There is a queue, If the child is not completed, the child's mounted will be called, because there may be child components in the child component. At this time, it will be temporarily stored. When the child is completed, it will be adjusted according to the child's parent. Mounted is the child first and then the parent.

Source code: when dom is rendered and updated, the patch method will be called. There is an insertedVnodeQueue array in the patch method, which will store all vnodes in insertedVnodeQueue. Finally, after the whole creation, the invokeInsertHook method will be called, and the collected insert hook s will be called in turn. In the patch, the createElm method will be called to create the element, and the createElm method will judge the element: if the element is a component, the createComponent method will be called to create the component, the init method of the component will be called to render the content of the current component, and the pendingInsert will be inserted into its insertedVnodeQueue through the initComponent method, and then it will be set to null, Finally, the invokechreatehooks method will be called to store vnode in insertedVnodeQueue; If the element is not a component, the createChildren method will be called to recursively traverse the child nodes (child components), and the createElm method will be called again in the createChildren method until the element is not a component.

When all components are completed, the invokeInsertHook method will be called, and the collected hooks will be called successively through the insert method, and mounted will be triggered in the insert method.

 

4, Source code:

1.patch

function patch (oldVnode, vnode, hydrating, removeOnly) {
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = [] // Defines an array of insert hook methods that collect all components

    if (isUndef(oldVnode)) {
      
      isInitialPatch = true
      createElm(vnode, insertedVnodeQueue) // Create element
    } 
    // Finally, the collected insert hook will be called in turn
    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
    return vnode.elm
  }

2.createElm

function createElm (
    vnode,
    insertedVnodeQueue,
    parentElm,
    refElm,
    nested,
    ownerArray,
    index
  ) {
    //Judge whether the element is a component. If the createComponent method is returned to true after execution, return. If it is not a component, continue to call the createchild method
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    } 
    // createChildren will recursively create child components
    createChildren(vnode, children, insertedVnodeQueue)
})

3.createComponent (used to create components)

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) // Call the init method of the component to render the content of the current component
        insert(parentElm, vnode.elm, refElm)
        if (isTrue(isReactivated)) {
          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
        }
        return true
      }
    }
  }

4.initComponent 

// Insert pendinginsert into your own 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

// Insert the vnode of the component into the array  
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 

// Finally, after all components are completed, call the collected insert hook in turn
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]) // Call the insert method
      }
    }
  }

7.insert 

// The mounted method will be called successively in the insert method
function insert (vnode) {
    var context = vnode.context;
    var componentInstance = vnode.componentInstance;
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true;
      callHook(componentInstance, 'mounted'); // Call mounted
    }
    if (vnode.data.keepAlive) {
      if (context._isMounted) {
        queueActivatedComponent(componentInstance);
      } else {
        activateChildComponent(componentInstance, true /* direct */);
      }
    }
  }

8.createChildren 

// Recursively traverse the child nodes, and then call the createElm method again
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. Destruction

Vue.prototype.$destroy = function () {
  callHook(vm, 'beforeDestroy') //
  vm.__patch__(vm._vnode, null) // Destroy your son first
  callHook(vm, 'destroyed')
}

 

Tags: Javascript Front-end Vue

Posted by CodeBuddy on Mon, 09 May 2022 23:06:28 +0300