1. The entrance

Since my native debugging code is built with NPM run dev, I can find the actual running commands in package.json

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
Copy the code

From our initial analysis, we can find the compile entry in scripts/config.js via 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

This leads us to the web/ entry-Runtime-with-Compiler.js file, which we find in the last line

export default Vue
Copy the code

The Vue is our final output constructor in the Vue. Where does it come from

entry-runtime-with-compiler.js -> web/runtime/index -> core/index -> core/instance/index

I finally found you, but I didn’t give up

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'

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')}this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
Copy the code

This time we can see Vue for what it really is. It’s a constructor, but it’s a bit simple. Just one line of code to initialize this._init(options), and we don’t even see where the _init method is defined.

The advantage of this is that the constructor logic is split into different parts, separated according to logic decoupling. Different Mixin functions provide different function methods for Vue, where the _init method is defined in initMixin.

2. Initialize the function

So let’s look at what the _init function does, it’s a lot of code, so I’m just going to comment on it, but we’re just going to focus on the main flow.

let uid = 0

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options? :Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    // a flag to avoid this being observed
    vm._isVue = true

    // merge options
    if (options && options._isComponent) {
      // Component instance skipped temporarily
      initInternalComponent(vm, options)
    } else {
      / / resolveConstructorOptions method inheritance from constructor of the superclass
      Options {components: {}, directives: {}, filters: {}}
      Options => Vue. Options is defined as initGlobalAPI(Vue) in core/index.js.
      // initGlobalAPI see 2.1
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

    /* istanbul ignore else */
    if(process.env.NODE_ENV ! = ='production') {
      // proxy The proxy VM is used to prompt errors and specify key names if the key is not defined on the instance but
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    _isDestroyed vm._inactive VM.$parent vm.$children
    / / see 2.2
    initLifecycle(vm)
    Register events defined in the component such as 
      
    / / see 2.3
    initEvents(vm)
    // Initialize the rendering function
    initRender(vm)
    // Call the component's beforeCreate hook
    callHook(vm, 'beforeCreate')
    // Inject will be skipped temporarily
    initInjections(vm) // resolve injections before data/props
    // See 2.4 for initialization data
    initState(vm)
    // provide skip temporarily
    initProvide(vm) // resolve provide after data/props
    The difference between beforeCreate and Created is the middle data initialization functions, especially initSate
    callHook(vm, 'created')

    // Mount the node
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
Copy the code

Now that we’ve learned about the only calling method in the constructor, the this._init method, we can basically see the logical flow of the instantiation. Because the methods called in the function are highly encapsulated, there is a lot of logic in the code, but a lot of code. In fact, we will look at some of the methods in the above process to further clarify the initialization logic.

2.1 initGlobalAPI

We mentioned vm.contructor.options in the initialization function earlier, but we didn’t see where to define it. In fact, when we looked for the definition of the constructor Vue earlier, Js -> web/runtime/index -> core/index -> core/instance/index. They’re doing a bunch of things in their own scope, and there’s a line of code in core/ Index

initGlobalAPI(Vue)
Copy the code

Let’s look at the logic of initGlobalAPI in core/global-api/index.js

export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () = > config
  Object.defineProperty(Vue, 'config', configDef)

  // Static utility functions that define Vue but are not part of the open API we try not to use
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  // Set delete nextTick is initialized here
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T= > {
    observe(obj)
    return obj
  }

  // Our main character Options is introduced
  Vue.options = Object.create(null)
  // ASSET_TYPES are defined in SHARE /constants ASSET_TYPES = [' Component ', 'directive', 'filter']
  ASSET_TYPES.forEach(type= > {
    Vue.options[type + 's'] = Object.create(null)})//
  Vue.options._base = Vue

  // KeepAlive logic is skipped first
  extend(Vue.options.components, builtInComponents)

  // Some common method definitions implement the use mixin extend method in functions through decoupling
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  Vue.directive Vue.component vue. filter is defined by iterating through ASSET_TYPES
  initAssetRegisters(Vue)
}
Copy the code

2.2 initLifecycle

How exactly is the lifecycle initialized at initLifecycle

export function initLifecycle (vm: Component) {
  const options = vm.$options

  // Look up for the real parent node
  // locate first non-abstract parent
  let parent = options.parent
  if(parent && ! options.abstract) {while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }

  $parent $root $children $refs, etc
  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {}

  // The lifecycle defines the associated attribute initialization
  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}
Copy the code

In fact, you can see that the initLifecycle function is a little simpler than we thought, it just defines the attributes associated with the instance and parent node and the lifecycle description attributes. This is understandable because the lifecycle hook functions are defined in the component and are triggered during initialization and rendering updates, so in theory hook calls should be created during the actual rendering process as well, like the callHook(VM, ‘created’) called in the _init function. Modify lifecycle properties and call functions in created hook calls.

2.3 initEvents

The initEvents function itself is a simple process for recording updateComponentListeners for functions defined on the component

export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}
Copy the code

2.4 initState

Finally, we will look at initState, which is an important function, to see what the framework does between beforeCreate and created

export function initState (vm: Component) {
  vm._watchers = []
  // This code is really short and a few short lines of code initialize props Methods data computed
  const opts = vm.$options
  / / see against 2.4.1
  if (opts.props) initProps(vm, opts.props)
  / / see 2.4.2
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    / / see 2.4.3
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)
  if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code

Against 2.4.1 initProps

How is initProps initialized

function initProps (vm: Component, propsOptions: Object) {
  // propsOptions defines the Props as {name: String}
  // propsData is the data received by the component in the form {name: 'Joke'}
  const propsData = vm.$options.propsData || {}
  const props = vm._props = {}

  // cache prop keys so that future props updates can iterate using Array
  // instead of dynamic object key enumeration.
  const keys = vm.$options._propKeys = []
  constisRoot = ! vm.$parent// root instance props should be converted
  if(! isRoot) { toggleObserving(false)}// Walk through the prop definition
  for (const key in propsOptions) {
    keys.push(key)
    // validateProp is used to get the prop value (propsData data or default value)
    const value = validateProp(key, propsOptions, propsData, vm)

    if(process.env.NODE_ENV ! = ='production') {
      const hyphenatedKey = hyphenate(key)
      if (isReservedAttribute(hyphenatedKey) ||
          config.isReservedAttr(hyphenatedKey)) {
        warn(
          `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
          vm
        )
      }
      // We can see our error indicating that the old friend does not allow the parent component to pass the prop value
      defineReactive(props, key, value, () = > {
        if(! isRoot && ! isUpdatingChildComponent) { warn(`Avoid mutating a prop directly since the value will be ` +
            `overwritten whenever the parent component re-renders. ` +
            `Instead, use a data or computed property based on the prop's ` +
            `value. Prop being mutated: "${key}"`,
            vm
          )
        }
      })
    } else {
      // Add responsiveness for props, where toggleObserving(false) is used for this function
      // When value is an object, responses are not added further recursively
      defineReactive(props, key, value)
    }
   
    // the vm proxy _props is the same as our props
    if(! (keyin vm)) {
      proxy(vm, `_props`, key)
    }
  }
  toggleObserving(true)}Copy the code

2.4.2 initMethods

InitMethods is much simpler

function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    if(process.env.NODE_ENV ! = ='production') {
      if (typeofmethods[key] ! = ='function') {
        warn(
          `Method "${key}" has type "The ${typeof methods[key]}" in the component definition. ` +
          `Did you reference the function correctly? `,
          vm
        )
      }
      if (props && hasOwn(props, key)) {
        warn(
          `Method "${key}" has already been defined as a prop.`,
          vm
        )
      }
      if ((key in vm) && isReserved(key)) {
        warn(
          `Method "${key}" conflicts with an existing Vue instance method. ` +
          `Avoid defining component methods that start with _ or $.`)}}// All the above are checksum errors in the development environment
    The only actual logic in the formal environment is to copy all methods under methods to VM and specify this as VM
    vm[key] = typeofmethods[key] ! = ='function' ? noop : bind(methods[key], vm)
  }
}
Copy the code

2.4.3 initData

Let’s move on to the logic of initData

function initData (vm: Component) {
  let data = vm.$options.data
  // If it is a function, point this to the VM and execute the function
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  
  // Returns an object verification prompt
  if(! isPlainObject(data)) { data = {} process.env.NODE_ENV ! = ='production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }

  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    // Check whether the methods and props keys are identical
    const key = keys[i]
    if(process.env.NODE_ENV ! = ='production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if(props && hasOwn(props, key)) { process.env.NODE_ENV ! = ='production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if(! isReserved(key)) {// VM proxy _data
      proxy(vm, `_data`, key)
    }
  }

  // Reactive data will be analyzed separately in the following article
  // observe data
  observe(data, true /* asRootData */)}Copy the code

2.4.4 initComputed and initWatch

We’ll get to watch later

3. The conclusion

Previously, we briefly analyzed the flow of Vue function instantiation.

  • Find the entry through entry-runtime-with-Compiler. js, and trace back to the Vue function definition

  • _init function flow

Initialize life cycle data -> Initialize component events -> Initialize render -> call beforeCreate -> Initialize data -> Call Created -> Mount node

  1. Vue static method initializes initGlobalAPI logic

  2. Instantiate the process-related function initLifecycle initEvents initState logic

  3. InitState Specifies the initialization logic of props Methods data

There are some processes that have not been analyzed, which I will continue in a later article

  1. InitRender initializes the render

  2. InitState initializes computed Watch in data

  3. Observe (data) data monitoring in initData

Last but not least, the code of the post is more. The analysis of the wrong place hope to help correct, there is not clear place can also be put forward, we communicate ~