The most difficult thing is to know yourself

preface

In Vue, each Vue instance goes through a process from creation to destruction, just like a human, from birth to death. A lot of things happen during this process, such as setting up data listeners, compiling templates, mounting components, etc. In Vue, the process of creating a Vue instance from its creation to its destruction is called the Vue instance life cycle, and Vue also provides different hook functions at different stages of the Vue instance life cycle, so that users can do extra things at different stages of the Vue instance life cycle.

The body of the

Let’s take a look at the official life cycle diagram:

beforeCreate

When creating a Vue instance, this._init(options) is executed internally:

vm.$options = mergeOptions( // Merge the configuration
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
)
initLifecycle(vm) // Initialize the life cycle
initEvents(vm) // Initialize the event center
initRender(vm) // Initialize render
callHook(vm, 'beforeCreate')
Copy the code
mergeOptions Merge configuration Combine Vue, user-defined, and instance properties, and the hook function merges into an array
initLifecycle(vm) Initialize the life cycle Mount some properties to the Vue instance and set default values,$parent,$rootThe more important
initEvents(vm) Initialize the event center In the registration event from the parent component to the child component, the custom event is passed to the child component, which is initialized when the child component is instantiated. Browser native events are handled in the parent component.
initRender(vm) Initialize the render Mount the Vue instance$slots, $scopedSlots, _c, $createElementAnd other attributes, will$attrs, $listenersTurn into responsive

Loading effects can be added at this stage and removed when created

created

initInjections(vm) // Initialize inject
initState(vm) // Initialize state
initProvide(vm) // initialize provide
callHook(vm, 'created')
Copy the code

InitProvide (VM) injects attributes into the current component, and initInjections(VM) is the initinjection remedy: use the bottom-up method to find the corresponding attributes injected by the upstream parent component. InitState (VM) initializes five options in order: props, Methods, data, computed, and watch.

initProps(vm, opts.props) Initialize the props Make the props responsive and proxy to the Vue instance
initMethods(vm, opts.methods) Initialize the methods Proxy methods to Vue instances
initData(vm) Initialize the data Assign responsivity to data and proxy to Vue instances
initComputed(vm, opts.computed) Initialize the computed Cycle to create a computed Watcher and collect it on the responsive properties that the handler depends on, and finally to put the computed proxy on the Vue instance
initWatch(vm, opts.watch) Initialize the watch The loop creates the user Watcher and collects it on the responsive properties it listens to

This phase can perform asynchronous requests for data methods, complete the initialization of the data

beforeMount

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

// src\platforms\web\entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el, hydrating) {
  el = el && query(el)
  
  if (el === document.body || el === document.documentElement) { process.env.NODE_ENV ! = ='production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
  
  if(! options.render) {if (template) {
          // Handle the selectors at the beginning of #, dom objects
      } else if (el) { // Get el's outerHTML as the template
          template = getOuterHTML(el)
      }
      if (template) { // Compile to generate the render functioncompileToFunctions(template, {... }}}return mount.call(this, el, hydrating)
}

// src\core\instance\lifecycle.js
export function mountComponent (vm, el, hydrating) {
  vm.$el = el
  if(! vm.$options.render) { vm.$options.render = createEmptyVNode } callHook(vm,'beforeMount')
  / /...
}
Copy the code

In this stage, we mainly did the following things:

  • judgeelIf it exists, it will be called if it exists$mountTo perform subsequent mounting operations
  • query(el)getelthedomElement reference
  • rightelMake a judgment, cannot beBody, HTMLThe element
  • judgerenderWhether or not the function is definedtemplateProcessing and template compilation generationrenderfunction
  • inVueInstance mount$elattribute
  • judgerenderWhether the function is defined or null if notVNode(The judgment here refers to the situation when the child component is mounted)

mounted

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

new Watcher(vm, updateComponent, noop, {
    before () {
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)

if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')}return vm
Copy the code

In this stage, render Watcher is created, vm._render() generates VNode, and vm._update() performs patch rendering view.

In this stage, DOM is loaded and mounted. DOM operations can be performed.

beforeUpdate

// src\core\observer\scheduler.js
function flushSchedulerQueue () {
    for (index = 0; index < queue.length; index++) {
        watcher = queue[index]
        if (watcher.before) {
          watcher.before()
        }
        id = watcher.id
        has[id] = null
        watcher.run()
    }
    / /...
    callUpdatedHooks(updatedQueue)
}
Copy the code

Mounted () {beforeUpdate () {before ();}} before () {before ();}} before () {before ()}}}

updated

function callUpdatedHooks (queue) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    if(vm._watcher === watcher && vm._isMounted && ! vm._isDestroyed) { callHook(vm,'updated')}}}Copy the code

In the beforeUpdate phase above, all Watcher calls callUpdatedHooks after executing the run method to loop through all Vue instances’ updated hook functions.

At this stage, data is updated and DOM is re-rendered. Unified processing of business can be carried out here.

beforeDestroy

// src\core\instance\lifecycle.js
Vue.prototype.$destroy = function () {
    const vm: Component = this
    if (vm._isBeingDestroyed) {
      return
    }
    callHook(vm, 'beforeDestroy')
    vm._isBeingDestroyed = true
    
    const parent = vm.$parent
    if(parent && ! parent._isBeingDestroyed && ! vm.$options.abstract) { remove(parent.$children, vm) }if (vm._watcher) {
      vm._watcher.teardown()
    }
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    
    if (vm._data.__ob__) {
      vm._data.__ob__.vmCount--
    }
    
    vm._isDestroyed = true
    
    vm.__patch__(vm._vnode, null)
    
    callHook(vm, 'destroyed')
    
    vm.$off()
    
    if (vm.$el) {
      vm.$el.__vue__ = null
    }
    
    if (vm.$vnode) {
      vm.$vnode.parent = null}}}Copy the code

We can see that the beforeDestroy hook function is called at the beginning of execution of this.$destroy(). When is this.$destroy() executed except when we call it manually?

🙋♂️ : Execute at the time of component destruction

🙎♀️ : When will the component be destroyed?

🙋♂️ : During patch phase, the component is destroyed when oldVNode needs to be removed during VNode rendering update

// src\core\vdom\create-component.js
var componentVNodeHooks = {
    destroy: function destroy (vnode) {
      var componentInstance = vnode.componentInstance;
      if(! componentInstance._isDestroyed) {if(! vnode.data.keepAlive) { componentInstance.$destroy(); }else {
          deactivateChildComponent(componentInstance, true /* direct */); }}}}Copy the code

When Vnode renders an update, it also fires the corresponding hook function at each stage while performing the related operation. When a component is destroyed, the destroy hook function is triggered to execute this.$destroy().

destroyed

The Destroyed phase does several things:

  • Will the currentVueInstance removed from its parent instance
  • Unload from the current instancewatcher
  • vm.__patch__(vm._vnode, null)againpatchFor manual callsthis.$destroy()In the case

Note: The removal of all custom event listeners, vm.$off(), is done after the Destroyed hook function is executed.

Finally, references to some related properties are removed, and the component is destroyed.

At the end

Nothing to say, Aoli!!