Vue life cycle details

Time:2020-1-14

Vue life cycle details

Vue life cycle process

In the beginning, users use new vue() to create root Vue instances, or Vue instantiation subcomponents call_initMethod (We call both examplesvm):

Function Vue (options) {// Vue constructor
    ...
    this._init(options)
}
...
Const sub = function (options) {// define subcomponent constructor
    this._init(options)
}

vmPrototype methods are called on instantiationthis._initMethod to initialize:

Vue.prototype._init = function(options) {
    VM. $options = mergeoptions (// merge options
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
    )
    ...
    Initlifecycle (VM) // start a series of initialization
    initEvents(vm)
    initRender(vm)
    Callhook (VM, 'beforecreate') // execute the beforecreate hook
    initInjections(vm)
    initState(vm)
    initProvide(vm)
    Callhook (VM, 'created') // execute the created hook
    ...
    if (vm.$options.el) {
        vm.$mount(vm.$options.el)
    }
}

beforeCreate

First of all, the user providedoptionsObject, parent defined on childeventprops(when a subcomponent is instantiated),vmPrototype approach, andVueConstructor built-in options combined into a newoptionsObject, assigning tovm.$options
Next, perform three initialization methods:

  1. Initlifecycle (VM): it is mainly used to confirm the parent-child relationship of components and initialize some instance properties. Find the parent component instance assigned tovm.$parentTake yourselfpushFor parent component$children
  2. Initevents (VM): the main function is to use the parent componentv-onor@Registered custom events are added to the private properties of the subcomponentvm._eventsMedium;
  3. Initrender (VM): the main function is to initialize therenderConversion of function tovnodeTwo methods ofvm._candvm.$createElement。 User definedrenderParameters of functionhNamelyvm.$createElementMethod, which can returnvnode。 When all the above operations are completed, it will be executedbeforeCreateHook function. At this time, the user can use thethisVisit tovm.$parentandvm.$createElementAnd so on.

created

Next, we will continue to perform three initialization methods:

  1. Initializations (VM): initializinginjectMakevmThe corresponding dependency can be accessed;
  2. Initstate (VM): initializes the state to be used, includingpropsmethodsdatacomputedwatchFive options. Call the correspondinginitMethod, usingvm.$optionsThe options provided in initialize these States, whereinitDataMethod will callobserve(data, true)Implementation ofdataIn fact, it is used to monitor the properties inObject.definePropertyMethod defines thegetterandsetterMethod;
  3. Initprovide (VM): initializationprovideMakevmYou can provide dependencies for subcomponents.

These three initialization methods are initialized firstinject, and then initializeprops/dataStatus, final initializationprovide, the purpose of which is toprops/dataUse ininjectContent injected in.
When all the above operations are completed, it will be executedcreatedHook function. At this time, the user can use thethisVisit tovmMediumpropsmethodsdatacomputedwatchandinjectAnd so on.

beforeMount

If the user provideselOption, it will be called directly when instantiatingvm.$mountMethod to mount:

if (vm.$options.el) {
    vm.$mount(vm.$options.el)
}

If not providedelOption, you need to call it manuallyvm.$mountMethod mount.vm.$mountMethod:

Runtime version:
Vue. Prototype. $mount = function (EL) {// initial definition
    return mountComponent(this, query(el));
}
Full version:
const mount = Vue.prototype.$mount
Vue. Prototype. $mount = function (EL) {// expand the compiled
    var options = this.$options;
    if(!options.render) {
        if(options.template) {
            ... // some judgments
        }Else if (EL) {// the El option passed in is not empty
            options.template = getOuterHTML(el);
        }
        
        if (options.template) {
                Options. Render = compiletofunctions (template,...). Render // compiles a template into a render function
        }
    }
    ...
    Return mount. Call (this, query (EL)) // Vue. Prototype. $mount. Call (this, query (EL))
}

In the full version ofvm.$mountMethod, if the user does not providerenderFunction, it willtemplateperhapsel.outerHTMLCompile intorenderFunction.
And thenmountComponentFunction:

export function mountComponent(vm, el) {
    vm.$el = el
    ...
    callHook(vm, 'beforeMount')
    ...
    const updateComponent = function () {
        VM. Update (VM. Render()) // call render function to generate vnode and mount it in HTML
    }
    ...
    if (vm.$vnode == null) {
        vm._isMounted = true;
        callHook(vm, 'mounted');
    }
}

If the user provideselOption, the real node used for mounting will be obtained, and this node will be assigned tovm.$elAttribute.
When all the above operations are completed, it will be executedbeforeMountHook function, if provided by the userelOption, you can use thethisVisit tovm.$elProperty, whose value iselThe real node provided.

mounted

staymountComponentMethod, executevm._renderMethod acquisitionvnode

Vue.prototype._render = function() {
    const vm = this
    const { render } = vm.$options

    const vnode = render.call(vm, vm.$createElement)
    
    return vnode
}

stayvm._renderMethodvm.$options.renderFunctions, passing in argumentsvm.$createElement(corresponding statementrenderParameter of functionh), get the return resultvnode
In the implementation of the followingrenderDuring function:

render(h) {
    return h(
        "Div", // tag name
        [// array of child nodes
            [
                [H ("H1", "title H1")], // the child nodes are also generated by the H function
                [h('h2', "title h2")]
            ],
            [
                H (obj, [// subcomponent passes obj instead of tag name
                    h("p", "paragraph")
                ])
            ]
        ]
    );
}

implementrenderFunction is called recursivelyhFunction,hThe function is based on theoptionsOption object generates avnodeIn order to later convert it to a real node.

Whether it is the first rendering when the root node is mounted or the page is updated after the data changes, theupdateComponentMethod._renderMethod returnedvnodeIt’s a tree structureJavaScriptObject, next inupdateComponentCall in_updateMake this virtualDOMTree to realDOMTree:

const updateComponent = function () {
    VM. Update (VM. Render()) // call render function to generate vnode and mount it in HTML
}

andvm._updateMethod willvm.__patch__Method returned trueDomNode assigned tovm.$el

Vue.prototype._update = function(vnode) {
    ...
    if (!prevVnode) {
        //First render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
    } else {
        // update
        vm.$el = vm.__patch__(prevVnode, vnode);
    }
    ...
}

tovm.__patch__Method passed in parametersvm.$elIt was before.mountComponentThe truth of assignment in methodDomElement, is the mount object.vm.__patch__Generates and inserts realityDom

Vue.prototype.__patch__ = createPatchFunction({ nodeOps, modules }) 

nodeOpsSome operations are nativeDomA collection of methods for,modulesyesclass/attrs/styleThe collection of corresponding hook methods when creating, updating, and destroying attributes such ascreatePatchFunctionFunction returns apatchFunction:

export function createPatchFunction(backend) {
    ...
    const { modules, nodeOps } = backend
    
    Return function patch (oldvnode, vnode) {// receives the 'patch' function of new and old vnodes
        ...
        //Isdef function: (V) = > V! = = undefined & & V! = = null
        Const isrealelement = isdef (oldvnode. NodeType) // is it a real DOM
        If (isrealelement) {// the first rendering of the incoming VM. $el is the real DOM
            Oldvnode = emptynodeat (oldvnode) // convert VM. $el to vnode format
        }
        ...
    }
}

callemptyNodeAtThevm.$elTranslate intoVNodeFormat.VNodeyesVueDefined virtual node class,vnodeyesVNodeClass.

function emptyNodeAt(elm) {
    return new VNode(
        Nodeops. TagName (ELM). Tolowercase(), // corresponds to the tag attribute
        {}, // corresponds to data
        [], // corresponds to children
        Undefined, // corresponding to text
        Elm // the real DOM is assigned to the elm attribute
    )
}
Packed:
{
    tag: 'div',
    Elm: '< div id = "app" >
}

Then continue to create realityDom

export function createPatchFunction(backend) { 
    ...
    return function patch (oldVnode, vnode) {
        Const insertedvnodequeue = [] // used to cache insertedvnode
        ...
        Const oldelm = oldvnode. Elm // the real DOM after packaging
        Const parentelm = nodeops. Parentnode (oldelm) // the first parent node is < body ></body>
        
        Createelm (// create real DOM
            Vnode, // the incoming vnode
            Insertedvnodequeue, // empty array
            parentElm,  // <body></body>
            Nodeops. Nextsibling (oldelm) // next sibling
        )
        
        Return vnode.elm // returns the real DOM, and then overwrites VM. $El in "update"
    }
}

createElmMethod to generate reality based on node typeDomNode, and insertparentElmMedium. andcreateElmMethod will callcreateChildrenMethod, andcreateChildrenMethod will callcreateElmMethod to generate the truth of child nodesDomNodes, formingcreateElmRecursive call to method:

function createElm(vnode, insertedVnodeQueue, parentElm, ...) {
    ...
    If (createcomponent (vnode, insertedvnodequeue, parentelm, refelm)) {// this step can be ignored at this time
        return 
    }
    ...
    //If the node to be created is an element node
    Vnode. Elm = nodeops. CreateElement (tag) // create an empty element to mount the child node first
    Createchildren (vnode, children, insertedvnodequeue) // call the 'createchildren' method to create a child node
    Insert (parentelm, vnode.elm, refelm) // insert the real element vnode.elm into the parent node
    ...
}

Recursively create child nodes, insert parent nodes, and finally generatevmRealityDomnodevnode.elm
When all the above operations are completed, it will be executedmountedHook function, in this case, you can use thethisVisit tovm.$elProperty, which is now virtualvnodeTransformed realityDom

activated

If we study examplesvmIs a component instance, and it is<keep-alive>Component package, then it will have two additional hook functionsactivatedanddeactivated。 We assumevmIs the root Vue instancerootA descendant component of.
stayrootWhen it is mounted, it will bepatchCall in methodcreateElmMethod generate realityDomNode and insert<body>rootParent node of).
If there is a child node, it is called firstcreateChildrenMethod, increateChildrenPass throughcreateElmMethod to generate the reality of each child nodeDomNode, and thenDomNode insertionrootOfDomNode:

function createChildren(vnode, children, insertedVnodeQueue) {
    if (Array.isArray(children)) {
        for (var i = 0; i < children.length; ++i) {
            Createelm (children [i], insertedvnodequeue, vnode.elm, null, true, children, I); // the parameter vnode.elm is passed to parentelm parameter
        }
    }
    ...
}

So back up therecreateElmMethod is used to create a child node. If the child node is a component, thecreateElmCall increateComponentMethod to initialize the subcomponent and generate an instance of the subcomponentvm), which is called by the initialization subcomponentinitHook (vnodeThere are 4. management hookinit, prepatch, insertanddestroyInrenderFunction generationvnodeWill be loaded tovnode.data.hookAbove).

function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
    var i = vnode.data;
    if (isDef(i)) {
        var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;
        if (isDef(i = i.hook) && isDef(i = i.init)) {
            I (vnode, false / * drafting * /); // pause createcomponent and start calling vnode.data.hook.init hook for initialization
        }
        if (isDef(vnode.componentInstance)) {
            //Wait for the init hook to finish executing. At this time, the VM has finished executing the $mount method. Therefore, in the initcomponent method, push vnode into insertedvnodequeue
            initComponent(vnode, insertedVnodeQueue);    
            Insert (parentelm, vnode.elm, refelm); // insert the real element vnode.elm into the parent node
            if (isTrue(isReactivated)) {
                reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);
            }
            return true
        }
    }
}

stayinitCall in hookSubConstructor instantiation subcomponent:

init: function init(vnode, hydrating) {
    ...
    //Call the 'sub' constructor to instantiate the subcomponent and execute the 'beforecreate' and 'created' hooks
    var child = vnode.componentInstance = createComponentInstanceForVnode(vnode, activeInstance);
    //Call VM. $mount, execute 'beforemount' hook, and then execute updatecomponent, repeat the above process
    child.$mount(hydrating ? vnode.elm : undefined, hydrating);    
},

After initialization, the subcomponent instance is calledvmOf$mountMethod to mount and executepatchMethod, invmOfpatchMethod will be called againcreateElmMethod generate realityDomAt this time, the sub component instance will be difficult to avoid executing againcreateComponentMethod:

function createElm(vnode, insertedVnodeQueue, parentElm, refElm) { 
    ...
    If (createcomponent (vnode, insertedvnodequeue, parentelm, refelm)) {// if the child node is a component, call the createcomponent method to initialize the child component; then in the 'patch' method of the child component, call the 'createelm' method
          return      
    }    
    //Continue to create real nodes
    ...    
    vnode.elm = nodeOps.createElement(tag) 
    Createchildren (vnode, children, insertedvnodequeue); // pause from here, and create elm child nodes in createchildren
    Insert (parentelm, vnode.elm, refelm); // insert the real element vnode.elm into the parent node
    ...
}

This timecreateComponentDoes not perform initialization, but returns directlyundefinedIn this way, you can continue to create real nodes. If the descendants still have components, it is another cycle
Therefore, the order of creation and mount of parent-child nodes is as follows:
fatherbeforeCreate= > fathercreated= > fatherbeforeMount= > sonbeforeCreate= > soncreated= > sonbeforeMount

Go back tomountedLife cyclecreatePatchFunctionMethod, which returns thepatchMethod, private variableinsertedVnodeQueueThevnode

function patch() {
    var insertedVnodeQueue = [];
    ...
    Invokeinserthook (vnode, insertedvnodequeue, isinitialpatch); // call the insert hook
    Return vnode.elm // the real DOM element
}
...
//`The patch method is the patch method in update,
// it returns the real Dom element to the $el of the root instance of Vue, then invokes the mounted hook of the root Vue instance in mountComponent (see the mountComponent and _update methods before).
Root. $El = in root. \
...
Callhook (root, 'mounted'); // in mountcomponent

vmyesrootDescendants,vm.$vnodeAlso inrootInstancepatchMethodinsertedVnodeQueueMedium. stayinvokeInsertHookFunction, these are calledvnodeOfinsertHook:

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; // cache insertedvnode
    } else {
        //Only the initial of the original instance is false, so it will be delayed to call the insert hook of all descendant components at the end of the root Vue instance patch method
        for (var i = 0; i < queue.length; ++i) {
            Queue [i]. Data. Hook. Insert (queue [i]); // call the insert hook of the cached insertedvnode
        }
    }
}

If the current call isvm.$vnode.data.hook.insertMethod:

Insert: function insert (vnode) {// enter VM. $vnode
    Var context = vnode. Context; // parent component instance
    Var componentinstance = vnode.componentinstance; // corresponding component instance VM of vnode
    if (!componentInstance._isMounted) {
        componentInstance._isMounted = true;
        Callhook (componentinstance, 'mounted'); // calls the VM's mounted hook function (so the child's mounted hook is called before the parent)
    }
    if (vnode.data.keepAlive) {        //true
        if (context._isMounted) {
            //Updating parent component
            Queueactivatedcomponent (componentinstance); // when the parent component is updated, push 'VM' to the Vue global variable activatedchildren and wait for the 'activated' hook function to execute
        } else {
            //Parent component mounting
            Activate childcomponent (componentinstance, true / * direct * /); // call the 'activated' hook function of 'VM'
        }
    }
}

It can be seen from this that,VueWill followrootInstancepatchMethodinsertedVnodeQueueinvnodeOrder of executionmountedHook. In the node tree, the lower the component is, the better the integrity isDomNode and insert parentDomNode, whichvnodeThe more we get first.pushreachinsertedVnodeQueueSo the first to execute itsmountedHook.
Therefore, the order of creating and mounting hooks for a complete parent-child node is as follows:
fatherbeforeCreate= > fathercreated= > fatherbeforeMount= > sonbeforeCreate= > soncreated= > sonbeforeMount= > sonmounted= > fathermounted

stayvm.$vnode.data.hook.insertInvoked in methodactivateChildComponentFunction will callvmAnd its descendantsactivatedHook function:

function activateChildComponent(vm, direct) {
    ...
    if (vm._inactive || vm._inactive === null) {
        vm._inactive = false;
        for (var i = 0; i < vm.$children.length; i++) {
            Activate child component (VM. $children [i]); // recursively calls the activated hook of the child component
        }
        Callhook (VM, 'activated'); // call the activated hook of VM
    }
}

stayvmFirst mount, callmountedAfter the hook function, it will callactivatedHook function.
aftervmOfactivatedThe hook function will be in thekeep-aliveWhen a component is activated, it is called when it is activated. The specific call time isflushSchedulerQueueFunction finishedqueueAll of themwatchersLater.

deactivated

vmOfdeactivatedThe hook function will be in thekeep-aliveCalled when the component is deactivated.
staypatchAt the end of the method, the old node is deleted:

function patch() {
    ...
    RemoveVnodes (parentElm, [oldVnode], 0, 0); // removeVnodes in invokeDestroyHook (oldVnode) to delete old nodes.
    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
    return vnode.elm
}

If you want to deletevnodeYesdestroyHook, callvnode.data.hook.destroy

function invokeDestroyHook(vnode) {
    var i, j;
    var data = vnode.data;
    if (isDef(data)) {
        if (isDef(i = data.hook) && isDef(i = i.destroy)) {
            I (vnode); // call the vnode.data.hook.destroy hook
        }
        ...
    }
}
destroy: function destroy(vnode) {
    var componentInstance = vnode.componentInstance;
    if (!componentInstance._isDestroyed) {
        if (!vnode.data.keepAlive) {                                
            Componentinstance. $destroy(); // call VM. $destroy()
        } else {
            Deactivatechildcomponent (componentinstance, true / * direct * /); // call the 'deactivated' hook of the subcomponent
        }
    }
}

call`vmOfdeactivatedHook, recursively calling thedeactivatedHook:

function deactivateChildComponent() {
    ...
    for (var i = 0; i < vm.$children.length; i++) {
        Deactivatechildcomponent (VM. $children [i]); // recursively calls the 'deactivated' hook of the subcomponent
    }
    Callhook (VM, 'deactivated'); // call the 'deactivated' hook
    ...
}

These operations are performed in thepatchMethod, parent componentpatchAfter, themountedperhapsupdatedHook.

beforeUpdate

Each component instance corresponds to onewatcherInstance, it is inmountComponentMethod, in themountedInstantiated before hook:

export function mountComponent(vm, el) {
    ...
    callHook(vm, 'beforeMount')
    ...
    const updateComponent = function () {
        vm._update(vm._render(), hydrating);
    };
    new Watcher(vm, updateComponent, noop, {
        Before: function before() {// execute before run
         if (vm._isMounted && !vm._isDestroyed) {
                Callhook (VM, 'BeforeUpdate'); // the BeforeUpdate hook waits for execution
            }
        }
    }, true /* isRenderWatcher */);
    ...
    callHook(vm, 'mounted');
}

If it isRenderWatchervm._watcherIt will be used to assign:

var Watcher = function Watcher (vm, expOrFn, cb, options, isRenderWatcher) {
    This.vm = VM; // associate components
    if (isRenderWatcher) {
        vm._watcher = this;
    }
    vm._watchers.push(this);
    ...
    this.before = options.before;
    ...
    if (typeof expOrFn === 'function') {
        This. Getter = exporfn; // that is, VM. Watcher. Getter = updatecomponent
    }
    This. Value = this. Lazy? Undefined: this. Get(); // this. Getter will be called in this. Get, so new Watcher will call updatecomponent immediately
}

watcherIn the process of component renderingContactThe data property of has been recorded as a dependency. After that, when the value of the dependency changes, thesetterMethod, thewatcher, so that it is associated with the component(vm)Re render.
Once the data changes are detected,VueA queue is opened and all data changes that occur in the same event loop are buffered. If the samewatcherTriggered multiple times, will only be pushed into the queue once.
When the current event cycle ends, the next event cycle begins,VueRefreshes the queue and performs the de duplicated work.VueWill try to usePromise.thenMutationObserverandsetImmediatePublished micro tasks to executequeueMediumwatcher

function flushSchedulerQueue () {
    Queue.sort (function (a, b) {return a.id - b.id;}); // queue is the declared variable in the Vue constructor
    ...
    for (index = 0; index < queue.length; index++) {
        watcher = queue[index];
        if (watcher.before) {
            Watcher. Before(); // execute the BeforeUpdate hook function
        }
        id = watcher.id;
        has[id] = null;
        Watcher. Run(); // execute watcher
        ...
    }
    ...
    // call component updated and activated hooks
    Callactivatedhooks (activatedchildren. Slice()); // execute the activated hook function
    Callupdatedhooks (queue. Slice()); // execute the updated hook function
}

Refresh according toidYesqueueMediumwatcherSort. This ensures that:

  1. fatherwatcherPlatoon leaderwatcherComponents are updated from parent to child. (because parents are always created before children, soidSmaller);
  2. In a component, the user declareswatchersAlways inrender watcherBefore, becauseuser watchersCreate first;
  3. If thewatcherDuring operation, a subcomponent is destroyed, and thewatcher

In executionwatcher.runMethod, thewatcher.beforeMethod to executebeforeUpdateHook function.

updated

In executionwatcher.runMethod, thewatcher.getterMethod, and one of themwatcher(vm._watcher)It’s about usvmItsgetterCan be updatedvmOfupdateComponentMethod:

Watcher.prototype.run = function run () {
        if (this.active) {
            VaR value = this. Get(); // call the watcher. Get method
            ...
        }
        ...
}
Watcher.prototype.get = function get () {
        ...
        try {
            Value = this.getter.call (VM, VM); // call the watcher.getter method
        }
        ...
}

callupdateComponentMethod

updateComponent = function () {
    vm._update(vm._render(), hydrating);
};

vm._renderMethod will be re executedrenderFunction generationvnodeAnd thenvm._updateMethod willvnodeTurn into realityDomMount toHTMLMedium, and overlayvm.$el
Wait for all the above operations to be completedflushSchedulerQueueAt the end of the function, theactivatedHook functions andvmOfupdatedHook function:

function flushSchedulerQueue () {
    ...
    Callactivatedhooks (activatedchildren. Slice()); // execute the activated hook function
    Callupdatedhooks (queue. Slice()); // execute the updated hook function
}

function callUpdatedHooks (queue) {
    var i = queue.length;
    while (i--) {
        var watcher = queue[i];
        var vm = watcher.vm;
        if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
            Callhook (VM, 'updated'); // execute the updated hook function
        }
    }
}

stayupdatedPass in hook functionthis.$elVisitedvm.$elThe value of the property is the updated realityDom
beforeUpdateandupdatedThe execution order of hook function is very good and opposite, becauseflushSchedulerQueueIn function is index increment processingqueueMediumwatcherYes, so executebeforeUpdateThe order and of hook functionsqueueinwatcherIn the same order; andcallUpdatedHooksFunction is executed in descending order of index_watcherAssociated instance’supdatedHook, andqueuein_watcherThe order is reversed.
Plus fatherwatcherPlatoon leaderwatcherSo if the parent and child components are updated in the same event cycle, the execution order of the lifecycle hook is:
fatherbeforeUpdate= > sonbeforeUpdate= > sonupdated= > fatherupdated

beforeDestroy

callvm.$destroyDestructionvmExample:

Vue.prototype.$destroy = function() {
    var vm = this;
    if (vm._isBeingDestroyed) {
        return
    }
    callHook(vm, 'beforeDestroy');
    vm._isBeingDestroyed = true;
    
    // remove self from parent
    var parent = vm.$parent;
    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
        remove(parent.$children, vm);
    }
    
    // teardown watchers
    if (vm._watcher) {
        vm._watcher.teardown();
    }
    var i = vm._watchers.length;
    while (i--) {
        vm._watchers[i].teardown();
    }
    
    // remove reference from data ob
    // frozen object may not have observer.
    if (vm._data.__ob__) {
        vm._data.__ob__.vmCount--;
    }
    
    // call the last hook...
    vm._isDestroyed = true;
    // invoke destroy hooks on current rendered tree
    vm.__patch__(vm._vnode, null);
    // fire destroyed hook
    callHook(vm, 'destroyed');
    // turn off all instance listeners.
    vm.$off();
    // remove __vue__ reference
    if (vm.$el) {
        vm.$el.__vue__ = null;
    }
    // release circular reference (#6759)
    if (vm.$vnode) {
        vm.$vnode.parent = null;
    }
};

In callbeforeDestroyThe hook was not destroyed before, so the instance is still fully available at this step.

destroyed

vm.$destroyThe operations performed are

  1. deletevm.$parent.$childrenMediumvm
  2. Destructionvm._watcher(render watcher), destroyingvm._watchers[i]All of themwatcher
  3. Delete references in data observer;
  4. calldestroyedHook function;

amongvm.__patch__(vm._vnode, null)All child instances can be destroyed.

Vue life cycle flowchart

Vue life cycle details

Vue parent child component lifecycle hook execution order

  1. Parent child component mount process: parentbeforeCreate= > fathercreated= > fatherbeforeMount= > sonbeforeCreate= > soncreated= > sonbeforeMount= > sonmounted= > fathermounted
  2. Sub components arekeep-alivePackage of components (ignoredkeep-aliveComponent), parent-child component mount process: parentbeforeCreate= > fathercreated= > fatherbeforeMount= > sonbeforeCreate= > soncreated= > sonbeforeMount= > sonmounted= > sonactivated= > fathermounted
  3. Modify only the data of the parent or child component:beforeUpdate => updated
  4. Modify the data of parent-child components in the same event cycle (regardless of the order): parentbeforeUpdate= > sonbeforeUpdate= > sonupdated= > fatherupdated
  5. The parent component passes the data to a prop of the child component, and they are the dependencies of the parent and child components respectively. When modifying the data of the parent component: parentbeforeUpdate= > sonbeforeUpdate= > sonupdated= > fatherupdated
  6. Subcomponentsv-showInstruction binds the data of the parent component. When modifying the data of the parent component: parentbeforeUpdate= > fatherupdated, subcomponent holdmountedState unchanged;
  7. Subcomponentsv-showThe instruction binds the data of the parent component, and the child component iskeep-aliveComponent package, when modifying the data of the parent component: parentbeforeUpdate= > fatherupdated, subcomponent holdactivatedState unchanged;
  8. Subcomponentsv-ifThe instruction binds the data of the parent component. When modifying the data of the parent component:

    1. True = > false: parentbeforeUpdate= > sonbeforeDestroy= > sondestroyed= > fatherupdated
    2. False = > true: parentbeforeUpdate= > sonbeforeCreate= > soncreated= > sonbeforeMount= > sonmounted= > fatherupdated
  9. Subcomponentsv-ifThe instruction binds the data of the parent component, and the child component iskeep-aliveComponent package, when modifying the data of the parent component:

    1. True = > false: parentbeforeUpdate= > sondeactivated= > fatherupdated
    2. First time false = > true: parentbeforeUpdate= > sonbeforeCreate= > soncreated= > sonbeforeMount= > sonmounted= > sonactivated= > fatherupdated
    3. False = > true again: parentbeforeUpdate= > sonactivated= > fatherupdated
  10. SubcomponentsisProperty binds the data of the parent component. The parent component switches the child component 1 to the child component 2
    fatherbeforeUpdate= > sub twobeforeCreate= > sub twocreated= > sub twobeforeMount= > sub twomounted= > fatherbeforeUpdate1 = 1beforeDestroy1 = 1destroyed= > fatherupdated= > fatherupdated
  11. SubcomponentsisProperty to bind the data of the parent component. The child component iskeep-aliveComponent package: the parent component switches sub component 1 to sub component 2:

    1. First time: FatherbeforeUpdate= > fatherbeforeUpdate= > sub twobeforeCreate= > sub twocreated= > sub twobeforeMount1 = 1deactivated= > sub twomounted= > sub twoactivated= > fatherupdated= > fatherupdated
    2. Again: FatherbeforeUpdate1 = 1deactivated= > sub twoactivated= > fatherupdated

Dynamic component triggers parent twicebeforeUpdateupdatedReasons:
In the first event loop, the parent component’s_watcherIn callingrenderFunction rebuild parentvnodeIn the process of:

Var render = function() {// render function from Vue compiling template
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c(
    "div",
    { attrs: { id: "app" } },
    [
      _c("img", {
        attrs: { alt: "Vue logo", src: require("./assets/logo.png") }
      }),
      _c("p", [_vm._v(_vm._s(_vm.message))]),
            //In the case of being wrapped by the keep alive component, when the vnode of the keep alive component is generated, the 'U watcher' of the parent component is triggered for the second time`
      _c("keep-alive", [_c(_vm.now, { tag: "component" })], 1)
            //In the case of not being wrapped by the keep alive component, when generating the 'vnode' of the child 2 component, the 'watcher' of the parent component is triggered for the second time`
            _c(_vm.now, { tag: "component" })
        ],
    1
  )
}

In fact, the keep alive component is more specific. It is also the child of the keep alive component and the sub-2 componentvnodeTime triggered_watcher
Then thiswatcherWill be inserted.queuePresent in ChinawactherBehind (according towacther.idThe size of is inserted in the correct location):

function queueWatcher(watcher) {
    var id = watcher.id;
    If (has [ID] = = null) {// in flushschedulerqueue, before executing watcher.run, has [ID] = null;
        Has [ID] = true; // so wacther with the same ID can be inserted into the queue
        if (!flushing) {
            queue.push(watcher);
        } else {
            // if already flushing, splice the watcher based on its id
            // if already past its id, it will be run next immediately.
            var i = queue.length - 1;
            while (i > index && queue[i].id > watcher.id) {
                i--;
            }
            queue.splice(i + 1, 0, watcher);
        }
    }
    ...
}

Wait for the presentwatcher.runAfter execution, execute it again.