Vue source parsing: What happens behind the creation of a Vue program

Keywords: Javascript Vue Attribute

Main Outline:

  • Viewing the Global Configuration of Vue.config from the initGlobal API Method
  • Searching for the Origin of the Constructive Function of Ancestor-Vue

Let's start with the most common vue code demo

<div id="app">
  {{ message }}
</div>
// js
var vm = new Vue({
  el: '#app',
  data: {
    message: 'hello vue'
  }
})

A Vue application has been created above; it is easy to see from the above that Vue is a constructor, and vm is an instantiated object constructed with this constructor. When instantiating, parameters are passed in, including el and data.
The above extends three questions:

  1. What does the Vue constructor look like?
  2. How can Vm implement the open API of vue in the source code?
  3. What happened to the parameters we passed into the constructor

These problems are the first steps to unlock the vue source code, so we might as well start looking for the implementation of these source codes through the vue source code entry.

Under the source src/platforms/web, there are different versions of build entry files, which export Vue from src/core/instance/index.
Let's first look at the answers that the entry document can give us:

// src/core/instance/index
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
initGlobalAPI(Vue)

This entry file does three things:

  1. The reference to. / instance/index exposes the source of vue, the constructor
  2. Invoke initGlobal API method, pass vue in, expand global static method for vue
  3. Exposing vue

The significance of this entry file is that before exposing vue, it extends the global static method to Vue through initGlobal API method. The external API corresponding to Vue is Vue.config, which includes the global configuration of Vue.

Vue.config.silent // Logs and Warnings
Vue.config.errorHandler // When this handler is called, error information and Vue instances can be obtained.
Vue.config.devtools // Configuration allows vue-devtools to check code
…..

Viewing the Global Configuration of Vue.config from the initGlobal API Method

The initGlobal API method defines the configDef object, and its getter method has a property value of config. The setter method gives a warning that modifications are not allowed. Finally, the config attribute is added to vue, and the attribute description returns the configDef object.

export function initGlobalAPI (Vue: GlobalAPI) {
    // config
    const configDef = {}
    configDef.get = () => config
    if (process.env.NODE_ENV !== 'production') {
        configDef.set = () => {
            warn(
                    'Do not replace the Vue.config object, set individual fields instead.'
                )
      }
}
Object.defineProperty(Vue, 'config', configDef) // Add config attributes

In addition, util attributes are defined, but they are not exposed to the outside world and are not recommended for external use.

Searching for the Origin of the Constructive Function of Ancestor-Vue

Knowing the constructor, you know what happens when new vue()
The following code is the construction method of Vue. We can intuitively see that the Vue constructor uses ES5 Function to implement classes, because we can extend many methods to Vue prototype by prototype, and split these methods into different files/modules, which is more conducive to code maintenance and collaborative development.
For example, in this file, you can see that Vue is passed as a parameter into the following ** Mixin methods, which define some functions on its prototype by receiving vue.

function Vue (options) {
    if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
        ) {
            warn('Vue is a constructor and should be called with the `new` keyword')
            //   vue must be an instantiated object of new vue()
           }
        console.log('options', options)

        this._init(options) // Call the internal_init method
}
initMixin(Vue) // Operations before the created lifecycle function
stateMixin(Vue) // Subscription and Publication of Static Data Using Defined Property
eventsMixin(Vue) // Instance event flow injection utilizes event flow construction of subscription publishing mode
lifecycleMixin(Vue) // 
renderMixin(Vue) // Implementing _render rendering virtual dom
export default Vue

The core of this constructor is this._init(options)

At this breakpoint, you can see that the parameter options come in just the parameters el and data that we passed in when we instantiated them outside.

new Vue({
    el: '#app',
      data: {
        message: 'hello vue'    
      }
})

This _init method comes from the initMixin function
After looking at this function, we sorted out several important nodes of the source code in the whole initialization phase.

  1. Initialize options parameters for merge configuration
  2. Initialization life cycle
  3. Initialization time system
  4. Initialize state, including data, props, computed, watcher

export function initMixin (Vue: Class<Component>) {

console.log('Vue', Vue)
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// The uid of a uid instantiation is incremented by 1
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */

...

// Identifying the current instance as a Vue instance with _isVue is done for subsequent observation
vm._isVue = true    
// Configure options and determine if it is the initialization of options for internal Component s?
if (options && options._isComponent) {
    // inside
    initInternalComponent(vm, options)
} else {
    // Non internal
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
    )
}
// Point this to vm._renderProxy in render
if (process.env.NODE_ENV !== 'production') {
    initProxy(vm)
} else {
    vm._renderProxy = vm
}
// expose real self
vm._self = vm
// Initialization life cycle
initLifecycle(vm)    
// Initialize event registration
initEvents(vm)
// Initialization rendering
initRender(vm)
// The beforeCreate hook function in the trigger drop function
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
// Initialize the state of vm, including data, props, computed, watcher, etc.
initState(vm)
initProvide(vm) // resolve provide after data/props
// vm has been created and the created hook function is dropped
callHook(vm, 'created')
/* istanbul ignore if */
…
// Mount instances
if (vm.$options.el) {
    vm.$mount(vm.$options.el)
    }
}

}

Posted by sean14592 on Sat, 04 May 2019 19:50:37 -0700