Yan Dong, Front End Engineer, Front End Technology Department, Wemicro. Aspiring to become a full stack development engineer or even architect, I have a long way to go. Exercise in your life to release stress and think about problems.

The article is long, so it is recommended to spend a whole block reading the analysis. In addition, because the length is too long, this paper is divided into three articles output, easy for everyone to understand and read.

preface

inPrevious articleIn the analysis, we analyze the overall operation mechanism of Vue, where we know that the Vue calls init() function during initialization:

In the instance.js file, the initMixin() method is called. Then we go to the definition of the initMixin function in init.js, and there is this code in the middle:

    // merge options
    if (options && options._isComponent) { // Execute initInternalComponent if the current Vue instance is a component
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options) // This method basically adds some attributes to vm.$options
    } else {
      // Otherwise, instantiate the Vue object and call mergeOptions
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
Copy the code

This code is the merge option, so exactly how to merge options?

First let’s look at instantiating Vue directly, that is:

      // Otherwise, instantiate the Vue object and call mergeOptions
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
Copy the code

As you can see, the mergeOptions function (parameter 1, parameter 2, parameter 3) is called

  • Parameter 1: resolveConstructorOptions (vm) constructor);
  • Parameter 2: the options | | {};
  • Parameter 3: VM

Parameter 2 is the options parameter 3 that we passed when we created the Vue instance, the Vue instance itself

1: so, basically see parameters resolveConstructorOptions (vm) constructor) functions.

This space to introduce, vue is how to parse the current instance constructor resolveConstructorOptions function of the options.

resolveConstructorOptions

Resolves options on the current instance constructor

/ * * *@param {*} Ctor: vm.constructor * This method is described in two cases * where Ctor is an underlying Vue constructor * and where Ctor is extended by the vue.extend method. * /
export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  // Ctor is a subclass of vue.extend
  if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions // Options on Vue constructor
    if(superOptions ! == cachedSuperOptions) {// super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}
Copy the code

New Vue() constructs a subclass

Let’s first look at the case where Ctor is the base Vue constructor, which subclasses new Vue()
export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  // Ctor is a subclass of vue.extend.return options
}
Copy the code

Ctor is the Vue constructor. Ctor. Options is the options on the Vue constructor.

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>new Vue</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    {{message}}
  </div>
  <script>
    var app = new Vue({
      el: '#app'.data: {
        message: 'Hello Vue! '
      },
      created: function (){
        console.log('this.constructor.options '.this.constructor.options)
      }
    })
  </script>
</body>
</html>
Copy the code

We print the options object on the Vue constructor:

We will see that there are four base properties on the base constructor: Components, directives, filters, _base. Where are these properties defined?

Since we are initializing the entire framework, we need the framework project to run at some point, so we go to the package.json file and run the command scripts: “dev”: “rollup -w -c scripts/config.js –environment TARGET:web-full-dev”,

Then navigate to scripts/config.js and find target: :web-full-dev

// Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd'.env: 'development'.alias: { he: './entity-decoder' },
    banner
  },
Copy the code

Can see the entrance to the file is: web/entry – the runtime – with – compiler. Js in platforms/web/entry – the runtime – with – compiler. Js, we see this one line of code: import Vue from ‘./runtime/index’

In the document:

import Vue from 'core/index'.import platformDirectives from './directives/index'
import platformComponents from './components/index'.// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
Copy the code

These are the vue. options Settings, and then we substitute the platformDirectives and platformComponents files respectivelyThe platformDirectives export the GLOBAL Vue directives V-model, V-show

The platformComponents file exports the Transition, TransitionGroup components of Vue’s global animation components.

Combine this with the screenshots we printed out above for vm.constructive. options

We see that in addition to the model and Show directives and Transition and TransitionGroup defined in the two files above, The KeepAlive component in components also has the filters, _base property that we didn’t see.

Let’s go back to the core/index file entry (where we found the instance.js file in the first article) and see:

import Vue from './instance/index' // The entry to the first article
import { initGlobalAPI } from './global-api/index'. initGlobalAPI(Vue) ...Copy the code

Go to global-api/index.js and search for vue. options:

  import { ASSET_TYPES } from 'shared/constants'
  import builtInComponents from '.. /components/index'. Vue.options =Object.create(null)
  ASSET_TYPES.forEach(type= > {
    Vue.options[type + 's'] = Object.create(null)})// this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)

// shared/constants
export const ASSET_TYPES = [
  'component'.'directive'.'filter'
]

/ /.. /components/index
import KeepAlive from './keep-alive'
export default {
  KeepAlive
}
Copy the code

So we find KeepAlive in filters,_base, and components. The options object of the Vue constructor is clear.

Here’s a summary of how to find the base Vue constructor options:

  • 1. Instantiate new Vue({})
  • 2. Print it outvm.constructor.optionsAnd foundcomponents.directives.filters._baseFour properties
  • 3, since this is the project initialization, find the package, JSON file, find the project startup command: NPM run dev
  • 4, the dev command line locates the scripts/config file and finds the entry fileweb/entry-runtime-with-compiler.js
  • 5. Through the entry fileimport Vue from './runtime/index', we first found componentsTransition,TransitionGroupComponents; Found in the Directives propertymodelandshowinstruction
  • 6. Pass the document againimport Vue from './runtime/index'And found thatimport Vue from 'core/index'
  • 7. Go back to the entry filecore/indexTo findinitGlobalApiAnd into theglobal-api/index.jsfile
  • 8. Search vue. options to find the rest of the componentsKeepAlive componentsAs well asfiltersand_baseattribute

After finding the path above, we finally found all the original options of the Vue constructor:components.directives.filters._base

Vue.extend constructs a subclass

Look at Ctor. Super, which subclasses Vue. Extend
The vue. extend function adds a super attribute to the instance
Vue.extend = function (extendOptions: Object) :Function {... Sub['super'] = Super
  ...
}

/ * * *@param {*} Ctor: ctor. super * This method is described in two cases * where Ctor is the base Vue constructor * and where Ctor is extended by the vue.extend method. * /
export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options // equivalent to vm.constructor.options
  // Ctor is a subclass of vue.extend
  if (Ctor.super) { // equivalent to vm.constructive.super
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions // Options on Vue constructor
    if(superOptions ! == cachedSuperOptions) {// super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}
Copy the code

Let’s look at creating an instance by vue.extend (), as an example:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>new Vue</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app2"></div>
  <script>
    var Profile = Vue.extend({
      template: '<p>123</p>',})console.log('new profile super '.new Profile().constructor.super)
    console.log('new profile superOptions'.new Profile().constructor.superOptions)
    console.log('new profile constructor '.new Profile().constructor)
    console.log('new profile constructor options'.new Profile().constructor.options)
    new Profile().$mount('#app2')
  </script>
</body>
</html>
Copy the code

We find the definition of the vue. extend function:

export function initExtend (Vue: GlobalAPI) {.../** * Class inheritance */
  Vue.extend = function (extendOptions: Object) :Function {
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
    ...
    const Sub = function VueComponent (options) {
      this._init(options) // The _init function on the Vue constructor is executed
    }
    Sub.prototype = Object.create(Super.prototype) // Inherit Super, which is Vue
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['super'] = Super
    ...

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}
Copy the code

As you can see, in the vue.extend function, Sub(VueComponent) inherits Super(Vue) and then adds it to the Sub constructor

  • Options: a blend of super. options and extendOptions(the options we passed in vue.extend ())
  • Super property: Pointing to super (Vue)
  • SuperOptions property: super.options
  • ExtendOptions property: Parameters passed in
  • SealedOptions properties: sub.options (see first)

At this point, let’s print it outsuper.superOptions.extendOptions.vm.constructor.vm.constructor.options

As you can see from the diagram of creating instances by vue.extend

  • vm.constructor.superThe value of is actually the Vue constructor. whilevm.constructor.superOptionsIs actually an option inherited from the underlying Vue constructor.
  • extendOptionsIs the argument we passed in.
  • Constructor is the constructor of a subclassVueComponent.
  • whilevm.constructor.optionsIn addition to the inherited four base attributes:components.directives. filters._baseIn addition, there are our own customtemplateProperties.
  • superOptions + extendOptions = vm.constructor.options(vm.$options)

So:

if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions // Options on Vue constructor
    if(superOptions ! == cachedSuperOptions) {// super option changed,
      // need to resolve new options. Replace your options with the latest
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
Copy the code

Merges the options passed in with the options in the parent Vue constructor and returns options.

Of course, there is a function in the middle, resolveModifiedOptions, that checks whether the options have changed.

summary

So, resolveConstructorOptions function is used to parse the current instance constructor options

The vue. extend method covers the knowledge of ES5 archetypal inheritance. If you are not familiar with it, check out the books offline.

In the next article, we will continue with the mergeOptions function, the outer merge option function.

Finally finished the analysis of merge options (three articles), every day out of sleep time to chew this, it is not easy to promote their growth, I hope to bring some inspiration to the cute, if there is any place feel not clear welcome to ask questions in the comments section below, discussion ~

See the little lovely people here, you can easily click a praise to encourage me to continue to create, creation is not easy, learn together, as soon as possible to achieve wealth freedom ~