This is the fourth day of my participation in the August Text Challenge.More challenges in August

Vue.extend

Case analysis

Extending a component is similar to the use of mixins, except that only one can be inherited

<! -- TestA-->
<script>
export default {
  created() {
   console.log('Base class, component for inheritance')}},</script>
Copy the code
<! --Demo--> <template> <div></div> </template> <script> import TestA from './test1.vue' export default { name: 'Dashboard', extends: TestA, // Single data() {return {visible: true, bar: 'ABC ',}}, created() {console.log(' shows the use of extends ')},} </script>Copy the code

When inherited, the component of extends takes precedence, so the result is run

Source code analysis

// core/global-api/extend.js
Vue.extend = function (extendOptions: Object) :Function {
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    if(process.env.NODE_ENV ! = ='production' && name) {
      validateComponentName(name)
    }

    const Sub = function VueComponent (options) {
      this._init(options)
    }
    // Inherit the Vue prototype object
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub // Set the constructor
    Sub.cid = cid++
      // Merge the root instance with the component option. The effect is that the global component can be used in this component
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    // Keep references to parent classes in child components
    Sub['super'] = Super
	// The props option is set in the sub component to initialize
    if (Sub.options.props) {
      initProps(Sub)
    }
    // The computed option is set in the child component to deinitialize
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // Save Vue's global API
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    ////////
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    // Returns the constructor of the child component
    return Sub
  }
}
Copy the code

Vue.mixin

Case analysis

var mixin = {
    data () {
        return {
            message: 'hello'.foo: 'abc'}},created: function () {
        console.log('Mixin object's hook is called')},methods: {
        foo: function () {
            console.log('foo')},conflicting: function () {
            console.log('from mixin')}}}new Vue({
    mixins: [mixin],  // Array, indicating that multiple can be mixed
    data() {
        return {
            message: 'goodbye'.bar: 'def'}},created: function () {
        console.log('Component hook called')},methods: {
        bar: function () {
            console.log('bar')},conflicting: function () {
            console.log('from self')}}})// => "Mixin hook is called"
// => "Component hook is called"
// => { message: "goodbye", foo: "abc", bar: "def" }
// vm.foo() // => "foo"
// vm.bar() // => "bar"
// vm.conflicting() // => "from self"
Copy the code

Source code analysis

Merges the mixin defined with options of the current component or global options. Note that the component has a higher priority when merging

// core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
    Vue.mixin = function (mixin: Object) {
        this.options = mergeOptions(this.options, mixin) // 
        return this}}Copy the code
  • MergeOptions () is used in both mixin and extend. Its main function is to merge two options. Its merge strategy is as follows:

    • For optons such as Data, Methods, Components, and directives, the component data takes precedence

    • The hook functions with the same name are merged into an array, and the hooks mixed into the object are called before the component’s own hooks

// core/util/options.js
export function mergeOptions(
  parent: Object,
  child: Object, vm? : Component) :Object {
  // Check whether the component name is valid
  if(process.env.NODE_ENV ! = ="production") {
    checkComponents(child);
  }

  if (typeof child === "function") {
    child = child.options;
  }
  // Standardize the treatment of props inject,directives, and add some default properties set in their own code to unify their code structure
  normalizeProps(child, vm);
  normalizeInject(child, vm);
  normalizeDirectives(child);

  // Use the _base attribute to determine whether the merge has been performed
  if(! child._base) {// With extends, recursive merge continues
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm);
    }
    // Use mixins to continue recursive merge
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm); }}}const options = {};
  let key;
  // First traverses the parent options to determine which merge strategy needs to be executed based on the key, and then merges
  for (key in parent) {
    mergeField(key);
  }
   // Iterate over sub-options, key section, merge strategy logic
   // Parent options are the options of the component
  for (key in child) {
   // There is no child options key in parent options. Merge child options keys
   // The parent options key takes precedence
    if(! hasOwn(parent, key)) {// Perform merge for child optionsmergeField(key); }}// Strats represents a collection of merge strategies for some of vue's options
  function mergeField(key) {
  // Check whether the merge policy is in the strats set by key. If not, add a default merge policy defaultStrat
    const strat = strats[key] || defaultStrat;
  // Execute the merge strategy for this key and add it to the new merged Options object
    options[key] = strat(parent[key], child[key], vm, key);
  }
  return options;
}
Copy the code
  • defaultStratDefault merge policy. The values of suboptions take precedence
// core/util/options.js
const defaultStrat = function (parentVal: any, childVal: any) :any {
  return childVal === undefined ? parentVal : childVal;
};
Copy the code

The articles

  • [Vue source]–$nextTick and asynchronous rendering (line by line comments)
  • [Vue source]–mixin and extend (line by line)
  • [Vue source]–Diff algorithm (line by line comment)
  • [Vue source]– How to listen for array changes (line by line comment)
  • [Vue source]– Reactive (bidirectional binding) principle (line by line comment)
  • [Vue source]– what is done for each lifecycle (line by line comments)
  • [Vue source]– How to generate virtual DOM (line by line comment)