This is the fifth day of my participation in the First Challenge 2022.

The background,

In the previous article, we explained the sequence of initGlobalAPI and new Vue by analyzing vue.js entry issues through the packaging of rollup in the Vue project. This sequence determines the source of properties such as vue.options in new Vue. Understanding the source of these objects helps to understand the execution of the Vue.

This question is also a common interview question.

Second, code directory structure

Take a look at the vue project structure. This is a simplified file structure, because we are only talking about the source code, and many of the subdirectories are omitted, benchmarks, etc. Don’t worry, only the SRC directory contains the source code.

.Benchmarks ├── Examples ├─ Flow ├── SRC // source directory │ ├── Compiler │ │ ├─ CodeGen │ │ ├ ─ ─ directives │ │ └ ─ ─ parser │ ├ ─ ─ this is a most important core │ │ ├ ─ ─ components │ │ ├ ─ ─ global - API │ │ ├ ─ ─ the instance │ │ │ └ ─ ─ Render - helpers │ │ ├ ─ ─ the observer │ │ ├ ─ ─ util │ │ └ ─ ─ vdom │ │ ├ ─ ─ helpers │ │ └ ─ ─ modules │ ├ ─ ─ platforms │ │ ├ ─ ─ web │ │ │ ├ ─ ─ the compiler │ │ │ │ ├ ─ ─ directives │ │ │ │ └ ─ ─ modules │ │ │ ├ ─ ─ the runtime │ │ │ │ ├ ─ ─ components │ │ │ │ ├ ─ ─ Directives │ │ │ │ └ ─ ─ modules │ │ │ ├ ─ ─ server │ │ │ │ ├ ─ ─ directives │ │ │ │ └ ─ ─ modules │ │ │ └ ─ ─ util │ │ └ ─ ─ weex Weex, omitted │ ├─ server omitted │ ├─ SFC │ ├─ shared ├─ test ├─ typesCopy the code

Two, breakpoint into Vue source code

2.1 Breakpoints in test.html

We wrote a debugger before new Vue in test.html, opened the console, refreshed, and saw the code stopped here:

2.2 Access to the source code entry file

This comes in handy since we prepared the sourcemap file by configuring rollup’s –sourcemap parameter in the first article. Through further click breakpoint according to the steps, into the Vue constructor of files: the SRC/core/instance/index. Js.

The Vue constructor

3.1 Vue declaration file SRC/core/instance/index. Js

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) The Vue constructor is a single line of code
}

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

export default Vue
Copy the code

From the code above, it is clear that the Vue constructor has only one critical line of code. But the amazing thing is that you don’t see the _init method declaration, so where is it declared? The answer is initMixin

3.2 the SRC/core/instance/init. InitMixin in js

As mentioned earlier, initMixn adds the _init method to the Vue constructor by exporting a function that takes the Vue constructor and extends the _init method:

// some import 
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options? :Object) {
    / /... detail of _init}}Copy the code

Four,_initThe logic of the

So let’s take a step-by-step look at what _init does.

4.1 Declaring VM Variables

Vm is a very important variable in Vue, keep in mind that VM is an instance of Vue, i.e. Vm = this:

// some import 
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options? :Object) {
    const vm: Component = this // VM is an instance of Vue
    // a uid
    vm._uid = uid++ // Each vue instance has a _uid, which is an incremented number
    vm._isVue = true // An identifier used to prevent being processed by data observation}}Copy the code

4.2 Handle option merging of components or root instances

4.2.1 Component Options

Component options are the objects we pass when creating a component, such as a child component declared in test.html. This is a component option and can be understood as a configuration item required to create a Vue component.

const sub = {
  template: ` 
      
{{ someKey + foo }}
`
.props: { someKey: { type: String.default: 'hhhhhhh'}},inject: ['foo']};Copy the code

4.2.2 Root instance options

That is, the option passed to the constructor when new Vue() is the root instance option, such as the object passed in test.html to create the Vue instance:

new Vue({ // This object is the root instance option
  el: '#app'.data: {
    msg: 'hello vue'
  },
  hahaha: 'hahahahahha'.provide: {
    foo: 'bar'
  },
  components: {
    someCom: sub
  }
})
Copy the code

4.2.3 Merge options

$options = vm.$options = vm.$options = vm.$options = vm.$options = vm.$options = vm

Vue.prototype._init = function (options? :Object) {
  // ...
  if (options && options._isComponent) {
    initInternalComponent(vm, options)
  } else {
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {},
      vm
    )
  }
 / /...
}
Copy the code

Here with the help of a few methods, mergerOptions and resolveConstructorOptions, including resolveConstructorOptions is to obtain the information such as the options from the constructor itself to instance reuse, temporarily not in the list.

4.3 Agent Instance Properties_renderProxyvmTheir own

When you access _renderProxy, you are accessing attributes of the VM itself, which will be used later in the rendering process.

Vue.prototype._init = function (options? :Object) {
  / /...
  if(process.env.NODE_ENV ! = ='production') {
    initProxy(vm); // The development environment with ES6 Proxy implementation
  } else {
    vm._renderProxy = vm
  }
  / /...
}
Copy the code

4.4 Performing a Series of initialization operations

Vue.prototype._init = function (options? :Object) {
  / /...
  // Initialize key properties such as _inactive/_isMounted/_isDestroyed,
  $parent/$children/$refs
  initLifecycle(vm)
  
  // Initializes the component's custom event, such as @some-event on the custom component
  initEvents(vm)
  
  // Initialize the most important render methods: vm._c and vm.$createElement,
  // Parse the slot in the component and mount it to the vm.$slots attribute
  initRender(vm)
  
  // Call the beforeCreate lifecycle hook
  callHook(vm, 'beforeCreate')
  
  // Initialize the inject option on the component, render this as result[key] = val,
  // Then do a data response to this result, proxy each key to the VM
  initInjections(vm) // resolve injections before data/props
  
  / / here is the key of the treatment response data type, the later said alone, is roughly processing props/methods/data/computed/watch
  initState(vm)
  
  // Parse the provide on the component. The provide is similar to the React Provider, which passes data deep across components
  // Again, proxy the parsing results to vm._provide;
  // To add a bit more, why is initProvide initialized after initinject?
  // He does this because inject is on child components, provide is on parent components, and parent components are provided before child components
  // Initialize it after inject without any problem because it takes the provide from the parent component,
  // The parent component's provide has already been initialized
  initProvide(vm) // resolve provide after data/props
  
  // Calls the Created lifecycle hook
  callHook(vm, 'created')
  / /...
}
Copy the code

El and $4.5 the mount

If the $options.el attribute exists, then $mount is executed.

Vue.prototype._init = function (options? : $mount () {$mount () {$mount () {$mount () {$mount (); $mount(vm.$options.el) {vm.$mount(vm.$options.el)}}Copy the code