🌟🌟🌟🌟🌟

Taste: peach oolong

Cooking time: 30min








This article starts from the basic knowledge, is divided into basic knowledge and source code interpretation of two parts, the basic knowledge of the developers have mastered their own jump.

Basic knowledge of

Vue life cycle

Nature has spring, summer, autumn and winter. People are born, old, sick and dead. Of course, excellent Vue also has its own life cycle.

The lifecycle of a Vue is the creation and destruction of a Vue instance.

Life cycle function

There are some functions called lifecycle that run during the lifecycle, giving developers the ability to add business code at different lifecycle stages.

They’re also called lifecycle hooks in some articles on the web, but what are hooks?

Hook function

When the system executes somewhere, it checks to see if there is a hook. If there is a hook, the callback will be executed.

This hook is not that hook.

Popular said, the hook is in the program is running, in a particular location and framework of developers to design good to tell us the current program has been running a hook to a specific location, will trigger a callback function, and provide us, that we can in the life cycle of a particular stage of the preparation of the relevant business code.

I have added relevant notes to the official picture, hoping to make it more clear to you, as shown below.

Don’t panic if you don’t understand it, although there are lots of comments, let’s go through them one by one.

In general, the Vue life cycle can be divided into the following eight phases:

BeforeCreate Before instance creation

Created The created instance is complete

BeforeMount mount ago

Mounted mounting is complete

BeforeUpdate before update

Updated completed

BeforeDestory destroyed before

Destoryed destruction completed

1.beforeCreate

This hook is the first one fired after new Vue(), and data and methods on Data, methods, computed, and Watch cannot be accessed in the current phase.

2.created

This hook occurs after the instance is created, and the current phase has completed the data observation, meaning that the data can be used and changed. Changing the data here does not trigger the updated function. You can do some initial data fetching, you can’t interact with the Dom at this stage, you can access the Dom via vm.$nextTick if you want to.

3.beforeMount

This hook occurs before the mount, after the template template has been imported into the rendering function for compilation. At this stage, the virtual Dom has been created and is about to start rendering. Data can also be changed at this point without triggering the updated.

4.mounted

This hook occurs after the mount is complete. In the current stage, the actual Dom is mounted, the data is bidirectional, the Dom node can be accessed, and the Dom can be manipulated using the $refs attribute. You can also send a request to the background and get the data back.

5.beforeUpdate

This hook is triggered before the update, when the reactive data is updated and the virtual DOM is re-rendered, and you can make changes to the data at this stage without causing re-rendering.

6.updated

This hook occurs after the update is complete, and the component Dom is updated for the current phase. It is important to avoid changing the data during this period, as this may result in an infinite loop of updates.

7.beforeDestroy

This hook occurs before the instance is destroyed, and the instance is fully usable in the current phase, where we can clean up the mess, such as clearing timers.

8.destroyed

This hook occurs after the instance is destroyed, and only the DOM shell is left. Components have been disassembled, data bindings removed, listeners removed, and subinstances destroyed.

Pay attention to the point

There are a few considerations to keep in mind when using the lifecycle.

1. Except for beforeCreate and Created hooks, all other hooks are not called during server-side rendering.

2. As mentioned above, do not modify the assigned data in data when updated, otherwise it will lead to an endless loop.

3. All lifecycle functions of Vue are automatically bound to the context of this. So, if you use the arrow function here, you get the parent scope that this points to, and you get an error. The reason is explained in the source section below.

The source code interpretation

Because the source code section of Vue contains a lot of content, this article only selects the key code related to the life cycle for parsing. Also strongly recommend learning Vue source, other content, because the framework is really good, attach a link [Vue. Js technology reveal] (ustbhuangyi. Making. IO/Vue – analysi… .

Let’s start with the source code to answer the fourth question above. Instead of the deleted part).

// src/core/instance/lifecycle.js
The callhook function calls all callbacks registered with a lifecycle hook in the current vUE component instance.
/ / vm: Vue instance
// hook: lifecycle name
export function callHook (vm: Component, hook: string) {
  pushTarget()
  const handlers = vm.$options[hook] 
  // Initializes the process of merging options, merging various lifecycle functions into options
  const info = `${hook} hook`
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  popTarget()
}

// src/core/util/error.js
export function invokeWithErrorHandling (handler: Function, context: any, args: null | any[], vm: any, info: string) {
  let res
  try {
    res = args ? handler.apply(context, args) : handler.call(context)
    if(res && ! res._isVue && isPromise(res) && ! res._handled) { res._handled =true}}catch (e) {
    handleError(e, vm, info)
  }
  return res
}
Copy the code

We can see from the above code that the invokeWithErrorHandling method is invoked in callHook. In invokeWithErrorHandling, apply and call are used to change this to point to, In arrow functions, the this pointer cannot be changed, so we cannot use arrow functions when writing life cycle functions. Please refer to my other article [detailed prescription for This “skin”] (juejin.cn/post/684490…). .

After answering the questions left above, we’ll go through each lifecycle one by one.

1. BeforeCreate and created

// src/core/instance/init
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options? : Object) {
    const vm: Component = this.// The merge options section has been omitted
    
    initLifecycle(vm)  
    $parent, $root, $children, and other lifecycle attributes are added to the VM object
    initEvents(vm) // Initializes the event-related attributes
    initRender(vm)  // THE VM adds attributes and methods related to the virtual DOM, slot, etc
    callHook(vm, 'beforeCreate')  // Call the beforeCreate hook
    // the initinjection (VM) and initProvide(VM) injections values defined in the parent component _provided into the child component, and these properties are not observed
    initInjections(vm) 
    initState(vm)   // Initialize data such as props, methods, data, watch, and computed
    initProvide(vm) 
    callHook(vm, 'created')  // Calls the created hook}}// src/core/instance/state
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)
  if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code

We can see that the beforeCreate hook call precedes initState, and initState is used to initialize properties like props, methods, data, computed, and watch.

Reading the source code makes it clear that we have no access to data on props, methods, data, computed, or Watch when using the beforeCreate hook. In Created.

2. BeforeMount and mounted

// The mountComponent core is to instantiate a render Watcher
// The updateComponent method is called in its callback
// Two core methods vm._render(generate virtual Dom) and vm._update(map to real Dom)
// src/core/instance/lifecycle
export function mountComponent (vm: Component, el: ? Element, hydrating? : boolean) :Component {
  vm.$el = el
  if(! vm.$options.render) { vm.$options.render = createEmptyVNode ... } callHook(vm,'beforeMount')  // Call beforeMount hook

  let updateComponent
  if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
    updateComponent = (a)= > {
    // A function that maps the virtual Dom to the real Dom.
    Vm._render () is called before vm._update. const vnode = vm._render() ... vm._update(vnode, hydrating) } }else {
    updateComponent = (a)= > {
      vm._update(vm._render(), hydrating)
    }
  }

  new Watcher(vm, updateComponent, noop, {
    before () {
     // Check whether mouted is completed and is not destroyed
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)

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

BeforeMount hook function is executed before vm._render() is used to render VNode, and mouted hook is executed after vm._update() is used to patch VNode to the real Dom. It is also clear why Dom was not properly acquired until mounted stage.

3. The beforeUpdate and updated

  // src/core/instance/lifecycle
 new Watcher(vm, updateComponent, noop, {
    before () {
     // Check whether mouted is completed and is not destroyed
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')  // Invoke the beforeUpdate hook}}},true /* isRenderWatcher */)
 
 // src/core/observer/scheduler 
 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) {// Only if the current watcher is vm._watcher (i.e. the current render watcher)
       // The updated hook function is executed if the component is mounted and is not destroyed.
       callHook(vm, 'updated')  // Calls the updated hook}}}Copy the code

The first code appears between beforeMount and Mounted hooks, so what’s going on in Watcher? When does the callUpdatedHooks function in the second code meet the criteria and perform the updated? So let’s move on.

// src/instance/observer/watcher.js
export default class Watcher {... constructor ( vm: Component,expOrFn: string | Function.cb: Function,
    options?: ?Object.// In its constructor, isRenderWatcher,
    // Then assign the current instance of watcher to vm._watcherisRenderWatcher? : boolean ) {// Push the current wathcer instance to vm._watchers,
    // vm._watcher is used specifically to listen for changes in vm data and then re-render,
    // So it's a rendering dependent watcher, so in the callUpdatedHooks function,
    // The updated hook function is executed only after the vm._watcher callback completes
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)... }Copy the code

Vue uses Watcher to monitor data changes on instances and control the rendering process.

4. BeforeDestroy and destroyed

  // src/core/instance/lifecycle.js
  // During $destroy, it executes vm.__patch__(vm._vnode, null).
  // Trigger the destruct hook function of its child component, such a recursive call,
  // The destroy hook is executed in the same order as the mounted procedure.
  Vue.prototype.$destroy = function () {
    const vm: Component = this
    if (vm._isBeingDestroyed) {
      return
    }
    callHook(vm, 'beforeDestroy')  // Call the beforeDestroy hook
    vm._isBeingDestroyed = true
    // Some destruction work
    const parent = vm.$parent
    if(parent && ! parent._isBeingDestroyed && ! vm.$options.abstract) { remove(parent.$children, vm) }/ / remove the watchers
    if (vm._watcher) {
      vm._watcher.teardown()
    }
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    ...
    vm._isDestroyed = true
    // Invoke the destroy hook on the current Rendered tree
    // If a subcomponent is found, it is destroyed first
    vm.__patch__(vm._vnode, null)
    callHook(vm, 'destroyed')  // Call the Destroyed hook
    // Close all instance listeners.
    vm.$off()
    // Remove the __vue__ reference
    if (vm.$el) {
      vm.$el.__vue__ = null
    }
    // Release circular references
    if (vm.$vnode) {
      vm.$vnode.parent = null}}}Copy the code

Through the above code, we understand the component destruction phase of the disassembly process, which will execute a __patch__ function, explain a lot of space, want to understand this part of the students can read the source code interpretation of the link to everyone.

In addition to the eight kinds of hooks, we can also find several less commonly used hooks on the official website, which are listed here.

Several less commonly used hooks

activated

Called when the keep-alive component is activated. This hook is not called during server-side rendering.

deactivated

Called when the keep-alive component is disabled. This hook is not called during server-side rendering.

errorCaptured

Called when an error from a descendant component is caught. The hook receives three parameters: the error object, the component instance where the error occurred, and a string containing information about the source of the error. This hook can return false to prevent further propagation of the error

You can modify the state of a component in this hook. It is therefore important to short-circuit something else in the template or render function to prevent the component from going into an infinite render loop when an error is caught.

communication

Welcome to my personal public communication, high-quality original articles will be pushed synchronously. Background reply welfare, you can receive welfare, you know ~

Your front canteen, remember to eat on time.