This is the 27th day of my participation in the More Text Challenge. For more details, see more text Challenge

One, foreword

In the last part, we mainly introduced the implementation of array dependency collection

This section describes the implementation of the Vue lifecycle


Two, Vue. Mixin introduction

1. Introduction to mixins

Mixins are often used in Vue2 to add some lifecycle to all componentsCopy the code

2. Use mixins

When vUE is initialized, the beforeCreate lifecycle hook is used to extend the function of the beforeCreate through the vue. mixin extension so that multiple BeForecreates will be merged in the actual implementationCopy the code

3. Usage of life cycle

// Use vue.mixin for global extension
Vue.mixin({
  beforeCreate(){
    console.log("Global: a mixin - beforeCreate")}})let vm = new Vue({
  el: '#app'.//
  // beforeCreate(){},
  // Use 2: array writing method: if there is more logic to classify, can split into multiple functions
  beforeCreate: [function(){
      console.log("Local: New Vue-beforecreate 1") // Module A is initialized
    },
    function(){
      console.log("Local: New Vue-beforecreate 2") // Module B is initialized}}]);Copy the code

Third, Vue’s Global API

1. Use of global API and instance API

// Global API: applies to all components
Vue.component()
// Instance API: applies only to the current component
new Vue({
  component:{}
})
Copy the code

2, global API implementation principle

When the new Vue component is initialized:

  1. Using the options API declaration, only the current component takes effect;
  2. Attributes declared globally by Vue.com Ponent are incorporated into each component and take effect globally.

Iv. Vue.mixin implementation

1. Add the mixin method

Create Vue global API module: SRC /global-api;

Create SRC /global-api/index.js and add mixi static methods to Vue:

//src/global-api/index.js

export function initGlobalAPI(Vue) {
  Vue.mixin = function (options) {}}Copy the code

To initialize the vue Global API, call it in SRC /index.js:

// src/index.js
import { initGlobalAPI } from "./global-api";
import { initMixin } from "./init";
import { lifeCycleMixin } from "./lifecycle";
import { renderMixin } from "./render";

function Vue(options){
  this._init(options);
}

initMixin(Vue)
renderMixin(Vue)
lifeCycleMixin(Vue)
initGlobalAPI(Vue) // Initialize the Global Api

export default Vue;
Copy the code

2. Implement the Global API

Options to store properties for global use:

// src/global-api/index.js

export function initGlobalAPI(Vue) {
  // Global properties: vue.options
  // Function: store mixin, Component, filte, directive properties
  Vue.options = {}; 
  Vue.mixin = function (options) {
    
  }
  Vue.component = function (options) {}
  Vue.filte = function (options) {}
  Vue.directive = function (options) {}}Copy the code

3. Merging strategy of multiple vue. mixins

Global mixins can also be called multiple times:

Vue.mixin({
  beforeCreate(){
    console.log("Global: mixin-beforecreate 1")
  }
})
Vue.mixin({
  beforeCreate(){
    console.log("Global: mixin-beforecreate 2")}})Copy the code

At this point, the global declaration needs to be merged:

Vue.mixin = function (options) {
    // Merge the options passed in multiple times with the global property vue. options
}
Copy the code

Merge strategy:

ParentVal :{} childVal:{beforeCreate:fn1} {beforeCreate:[fn1]} ParentVal :{beforeCreate:[fn1]} childVal:{beforeCreate:fn2} {beforeCreate:[fn1,fn2]} Therefore, each merge needs to loop father (old value) and son (new value) to merge in turn. When the new value exists, the old value does not exist: add to the old valueCopy the code

Add the tool method mergeOptions to SRC /utils.js:

// src/utils.js

/** * Object merge: merge childVal into parentVal *@param {*} ParentVal The parent value - the old value *@param {*} ChildVal Child value - new value */
export function mergeOptions(parentVal, childVal) {
  let options = {};
  for(let key in parentVal){
    mergeFiled(key);
  }
  for(let key in childVal){
    // When the new value exists and the old value does not exist: add to the old value
    if(!parentVal.hasOwnProperty(key)){
      mergeFiled(key);
    }
  }
  function mergeFiled(key) {
    // Default merge method: overwrite the old value with the new value first
    options[key] = childVal[key] || parentVal[key]
  }
  return options;
}
Copy the code

4. Lifecycle merge strategy

Policy pattern: The combination of different life cycles is distinguished by different policies

// src/utils.js

let strats = {};  // Store all policies
let lifeCycle = [
  'beforeCreate'.'created'.'beforeMount'.'mounted'
];
lifeCycle.forEach(hook= > {
  // Create a lifecycle merge policy
  strats[hook] = function (parentVal, childVal) {
    if(childVal){ // Sons have values that need to be merged
      if(parentVal){
        // Father and son have values: father must be an array, add son to father
        return parentVal.concat(childVal);  
      }else{
        // The son has a value, but the father has no value: put the son in the new array
        // Note: If the lifecycle function is passed in as an array, there is no need to package it as an array
        if(Array.isArray(childVal)){
          return childVal;
        }else{
          return[childVal]; }}}else{  // There is no value for the son, no need to merge, directly return the father
      returnparentVal; }}})Copy the code
// src/global-api/index.js

export function initGlobalAPI(Vue) {
  // Global properties: vue.options
  // Function: store mixin, Component, filte, directive properties
  Vue.options = {}; 
  Vue.mixin = function (options) {
    this.options = mergeOptions(this.options, options);
    console.log("Print mixin merged Options".this.options);
    return this;  // Return this, providing the chained call
  }
  Vue.component = function (options) {}
  Vue.filte = function (options) {}
  Vue.directive = function (options) {}}Copy the code

5, test

Testing the lifecycle merge results in vue. mixin:


Five, global and instance lifecycle merge

After the global lifecycle merge is complete, the local declaration in the new Vuechu initialization is merged

When new Vue is initialized, the _init prototype method is entered:

// src/init.js#initMixin

Vue.prototype._init = function (options) {
    const vm = this;
    // In this case, you need to use the global options merged with the mixin to merge againvm.$options = mergeOptions(vm.constructor.options, options); . }Copy the code

Print vm.$options to see the merged result:

Question: What is the difference between vm. Constructor. Options and vue. options?

Vm may be a subclass of VM: a subclass of Vue may have enhanced Vue; Child components may inherit Vue; Options means Vue; Vm. Constructor refers to the constructor of a subclass (subcomponent);Copy the code

Vi. Realization of life cycle

1. Create the lifecycle execution function

In the SRC /lifecycle. Js lifecycle module, create a callHook function that executes the lifecycle hook:

// src/lifecycle.js

/** * Execute the lifecycle hook * fetch the corresponding array of lifecycle functions from $options and execute *@param {*} Vm Vue instance *@param {*} Hook life cycle */
export function callHook(vm, hook){
  // Get the array of functions corresponding to the lifecycle
  let handlers = vm.$options[hook];
  if(handlers){
    handlers.forEach(fn= > {
      fn.call(vm);  // This in the lifecycle refers to the VM instance}}})Copy the code

Add lifecycle hooks

  • When the view is rendered, the hook: beforeCreate is called
  • When the view is updated, the hook: created is called
  • When the view is mounted, call hook: mounted
// src/lifecycle.js

export function mountComponent(vm) {
  // vm._render() : calls the render method
  // vm._update: updates the virtual node to the page
  // Initialize the process
  // vm._update(vm._render());  
  / / modification
  let updateComponent = () = >{
    vm._update(vm._render());  
  }

  // Call the hook beforeCreate before the view is rendered
  callHook(vm, 'beforeCreate');

  // Render watcher: Each component has one watcher
  new Watcher(vm, updateComponent, () = >{
    console.log('Watcher-update')
    // When the view is updated, call the: created hook
    callHook(vm, 'created');
  },true)

   // When the view is mounted, call hook: mounted
   callHook(vm, 'mounted');
}
Copy the code
  • Watcher calls the hook before making a view update: beforeUpdate
  • When the view has been updated, call the hook: updated
// src/scheduler.js

/** * Flush the queue: execute all watcher.run and clear the queue; * /
function flushschedulerQueue() {
  // execute the life cycle beforeUpdate: beforeUpdate
  queue.forEach(watcher= > watcher.run()) // Trigger view updates in turn
  queue = [];       // reset
  has = {};         // reset
  pending = false;  // reset
  // Update completed, execute the life cycle: updated
}
Copy the code

3. Test the lifecycle execution process

Two beforeCreate hooks in vue. mixin; 2 beforeCreate hooks in new Vue; The execution is completed in the sequence after the merge;Copy the code


Seven, the end

In this paper, the realization of Vue life cycle mainly involves the following points:

  • Vue. Mixin is introduced
  • Vue Global API
  • Vue. Mixins

The next part is the process analysis of DIFF algorithm


Maintenance logs:

20210708: Fixed “4-4, lifecycle merge policy”, when the lifecycle function is an array, there is no need to wrap twice

20210806: Fixed typography problem