Handwritten vue2 0 source code Mixin mixing principle

preface

This article is mainly written by vue2 0 source code Mixin mixing principle

In the last article, we mainly introduced Vue Asynchronous update principle The core is to use nextTick to realize asynchronous queue. This article mainly includes Mixin mixing, which is a very key api in Vue. It plays an important role in merging options during Vue initialization

Applicable crowd: students who don't have time to see the official source code or who are ignorant of the source code and don't want to see it

text

Vue.mixin({
  created() {
    console.log("I'm a global Infiltrator");
  },
});

// Vue instantiation
let vm = new Vue({
  el: "#app",
  data() {
    return {
      a: { a: { a: { b: 456 } } },
      aa: 1,
      bb: 2,
    };
  },
  created() {
    console.log("I'm my own");
  },
  template: `<div id="a">hello I wrote it myself Vue{{name}}
          </div>`,
});
Copy code

When we want to reuse a piece of business code logic in Vue, we often use the method of mixing, but do you know the principle of mixing, the order of mixing and the merging strategy of different options? Let's write it together

1. Define global Mixin function

// src/global-api/mixin.js

import {mergeOptions} from '../util/index'
export default function initMixin(Vue){
  Vue.mixin = function (mixin) {
    //   Merge objects
      this.options=mergeOptions(this.options,mixin)
  };
}
};
Copy code

Create a new global API folder and define mixin as the global method of Vue. The core method is to use mergeOptions to mix the incoming options into your own options

// src/index.js
import { initMixin } from "./init.js";
// Vue is a constructor instantiated by the new keyword
function Vue(options) {
  // Vue initialization starts here
  this._init(options);
}
// This practice is conducive to code segmentation
initMixin(Vue);
export default Vue;
Copy code

Then, the initMixin method is introduced into the Vue entry file

2.mergeOptions method

// src/util/index.js
// Define lifecycle
export const LIFECYCLE_HOOKS = [
  "beforeCreate",
  "created",
  "beforeMount",
  "mounted",
  "beforeUpdate",
  "updated",
  "beforeDestroy",
  "destroyed",
];

// Merge strategy
const strats = {};

//Lifecycle consolidation strategy
function mergeHook(parentVal, childVal) {
  // If you have a son
  if (childVal) {
    if (parentVal) {
      // Merge into an array
      return parentVal.concat(childVal);
    } else {
      // Wrap into an array
      return [childVal];
    }
  } else {
    return parentVal;
  }
}

// Add merge policy for lifecycle
LIFECYCLE_HOOKS.forEach((hook) => {
  strats[hook] = mergeHook;
});

// mixin core method
export function mergeOptions(parent, child) {
  const options = {};
  // Traversal father
  for (let k in parent) {
    mergeFiled(k);
  }
  // A father has no son
  for (let k in child) {
    if (!parent.hasOwnProperty(k)) {
      mergeFiled(k);
    }
  }

  //Real merge field method
  function mergeFiled(k) {
    if (strats[k]) {
      options[k] = strats[k](parent[k], child[k]);
    } else {
      // Default policy
      options[k] = child[k] ? child[k] : parent[k];
    }
  }
  return options;
}
Copy code

Let's first focus on the mergeOptions method, which is mainly used to traverse the attributes of father and son for merging. If the merged options have their own merging strategy, then the corresponding merging strategy is used

Let's take a look at the merge strategy of the life cycle here. mergeHook obviously mixes all the life cycles into an array and calls them in turn

3. Call of life cycle

// src/lifecycle.js

export function callHook(vm, hook) {
  // Execute the methods corresponding to the life cycle in turn
  const handlers = vm.$options[hook];
  if (handlers) {
    for (let i = 0; i < handlers.length; i++) {
      handlers[i].call(vm); //this in the life cycle points to the current instance
    }
  }
}
Copy code

Call by traversing the life cycle above $options in turn

// src/init.js

Vue.prototype._init = function (options) {
  const vm = this;
  // this here represents the call_ Object of init method (instance object)
  //  this.$options are the attributes and global Vue that are passed in when the user creates new Vue Options merged results

  vm.$options = mergeOptions(vm.constructor.options, options);
  callHook(vm, "beforeCreate"); //Before initializing data
  // Initialization status
  initState(vm);
  callHook(vm, "created"); //After initializing data
  // Template rendering if there is el attribute
  if (vm.$options.el) {
    vm.$mount(vm.$options.el);
  }
};
Copy code

During init initialization, call mergeOptions to merge options, and then use callHook to execute the relevant methods passed in by the user where the life cycle needs to be called

// src/lifecycle.js
export function mountComponent(vm, el) {
  vm.$el = el;
  // Introduce the concept of watcher. Here, register a render watcher to execute VM_ The update (VM. _render()) method renders the view
  callHook(vm, "beforeMount"); //Before initial rendering
  let updateComponent = () => {
    vm._update(vm._render());
  };
  new Watcher(
    vm,
    updateComponent,
    () => {
      callHook(vm, "beforeUpdate"); //Before update
    },
    true
  );
  callHook(vm, "mounted"); //After rendering
}
Copy code

Call the relevant lifecycle callHook in the mountComponent method

4. Mixed mind map

Summary

So far, Vue's hybrid prototype has been written. In fact, the core is the object merging and the merging strategy of different options. At present, it only demonstrates the merging strategy of the life cycle. Later, when it comes to the component, it will talk about the merging strategy related to the component. You can look at the mind map and write the core code yourself. In case of incomprehension or controversy, you are welcome to comment and leave a message

Finally, if you think this article is helpful, remember to praise Sanlian Oh, thank you very much!

Keywords: Vue.js

Added by inerte on Fri, 04 Mar 2022 22:11:42 +0200