The problem background

Recently, after loading a component remotely, the browser console reported an error during component initialization:

So follow the code flow to see why the error is reported. Code to locate the error:

const originalVal = comp.props[key].default || ' ';
Copy the code

Where comp is the component published to the CDN after compiling the Vue single file component that is loaded remotely. We know that a Vue single file component will normally become an object with this data structure after being processed by the Vue-Loader:

The overall structure is similar to what we normally write for options. This object is rendered as a common Vue h function. However, after looking at the object loaded from the remote environment this time, it shows:

If you are familiar with Vue, you can probably see the problem at a glance. However, because I usually pay more attention to the responsive principle and various interface apis of Vue, I lack a little understanding of Vue as a whole.

The root cause of the problem is that the remote component was updated by someone else. After the update, the component is not written in export default Options. Export default vue.extend (options);

Why is it normal for developers to test themselves through component rendering?

Vue components

To understand the above specific reasons in detail, you need to take a deeper look at the Vue component. Vue componentization is often associated with Vue.component, whereas vue.extend is less commonly used.

Vue.extend Principle analysis

When I first saw the extend method, I actually thought it was strange, but after a careful understanding, I felt that the name was quite appropriate.

Is the extends keyword in Java, said the meaning of inheritance, Vue. Here the extend also said the meaning of inheritance, the extend in Vue on the basis of the constructor to create a new constructor

For reference, Vue implements the extend method in Version2.0.0. In fact, the overall flow of extend is fairly clear (see a few comments given in the code).

Prototype chain inheritance is implemented primarily through the object.create method (for the use of object.create, see the previous article: When called in new mode, it is initialized by calling the _init method on the Vue prototype chain. The following process is basically the same as new Vue(options) creating a Vue instance.

Vue.extend = function (extendOptions) {
    extendOptions = extendOptions || {};
    var Super = this;
    var isFirstExtend = Super.cid === 0;
    if (isFirstExtend && extendOptions._Ctor) {
      return extendOptions._Ctor
    }
    var name = extendOptions.name || Super.options.name;
    // 1. Define the extend return object
    var Sub = function VueComponent (options) {
      this._init(options);
    };
    // 1.1 Inherit the prototype chain through the object.create method
    Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;
    Sub.cid = cid++;
    // 2. Set options on the constructor to store the configuration
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    );
    Sub['super'] = Super;
    Sub.extend = Super.extend;
    config._assetTypes.forEach(function (type) {
      Sub[type] = Super[type];
    });
    if (name) {
      Sub.options.components[name] = Sub;
    }
    Sub.superOptions = Super.options;
    Sub.extendOptions = extendOptions;
    if (isFirstExtend) {
      extendOptions._Ctor = Sub;
    }
    // 3. Return the new constructor
    return Sub
};
Copy the code

Vue.com Ponent principle analysis

Vue.com Ponent is one of the most frequently used methods for component registration, and most people are familiar with calling methods. A brief introduction to Vue 2.0.0 source code (removed some branches in extranet Component functions) for comparison with vue.extend

Vue[type] = function (id, definition) {
  if(! definition) {// Get the component directly
    return this.options[type + 's'][id]
  } else {
    if (type === 'component' && isPlainObject(definition)) {
      definition.name = definition.name || id;
      // Define the constructor of the component
      definition = Vue.extend(definition);
    }
    this.options[type + 's'][id] = definition;
    return definition
  }
};
Copy the code

The following information can be obtained from the code implementation in Vue:

  1. The Vue.component function is a second wrapper on vue.extend, after obtaining the component’s constructor (vue.extend returns a constructor), and placing the constructor on the options property of Vue
  2. The corresponding component constructor can be obtained directly from the Vue.component function
  3. The Vue.component function determines whether the component exists

Why change it to vue.extend (options) and test component rendering is still normal?

This is because Vue uses the h function (createElement method) to judge the parameters passed in, and then classifiable them. In fact, the final result is still converted into the component constructor. However, our own code logic does not judge and handle components in the form of constructors, resulting in exceptions.

Generally speaking, there are two solutions:

  • Specification of the internal component writing, unified into a format, so that the development of students in writing code will be more clear
  • Drawing lessons from Vue’s practice, the two component writing methods are compatible

conclusion

The vue. extend method creates a constructor for a component that is called by new to obtain an instance object of the component.

Options: Options: options: options: options: Options: Options: Options: Options: Options The specific conversion relationship can be referred to the following figure:

stateDiagram-v2

Options --> Vue
Options --> Vue.extend
Options --> Vue.component
Vue.component-->Vue.extend
Vue --> instance
Vue.extend --> instance

PS. The above is some personal understanding of Vue components, there are improper places please do not hesitate to comment ~