Vue.js is an incremental framework for building user interfaces. It is lightweight and easy to learn and many developers love it. Understanding the source code helps us to have a deep understanding of VUE. Knowing what is and why is the only way for every engineer to advance. Without further ado, let’s get to the point.

I. Module overview

Vue source code is mainly divided into 6 modules

Module name instructions
compiler Compile related
core Vue core code
platforms Platforms, currently web and WEEX
server Server side rendering
sfc .vue file parsing
shared To share code

Two. Main entrance analysis

Vue uses rollup as the packaging tool, and I first look at scripts in package.json.

package.json

"scripts": {
    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"."dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev"."dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm"."dev:test": "karma start test/unit/karma.dev.config.js"."dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer"."dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler "."dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework"."dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory"."dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler "."build": "node scripts/build.js"."build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer"."build:weex": "npm run build -- weex"."test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex"."test:unit": "karma start test/unit/karma.unit.config.js"."test:cover": "karma start test/unit/karma.cover.config.js"."test:e2e": "npm run build -- web-full-prod,web-server-basic-renderer && node test/e2e/runner.js"."test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.js"."test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.js"."test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2"."test:types": "tsc -p ./types/test/tsconfig.json"."lint": "eslint src scripts test"."flow": "flow check"."sauce": "karma start test/unit/karma.sauce.config.js"."bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js"."release": "bash scripts/release.sh"."release:weex": "bash scripts/release-weex.sh"."release:note": "node scripts/gen-release-note.js"."commit": "git-cz"
  },
Copy the code

The build command is node scripts/build.js

Scripts /build.js trunk code is as follows:

let builds = require('./config').getAllBuilds();
// Omit n lines here
build(builds)
Copy the code

The getAllBuilds method is available from config.js, and here we look at the Runtime + Compiler version

The VUE compiler can be divided into two cases (on the Web side) :

  • Use vue-Loader in build-time Compiler, short for build-time Compiler.
  • In vUE runtime, go to Compiler, or runtime Compiler for short.

Here, we consider the full vUE source code and choose the Runtime + runtime Compiler version

// Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.min.js'),
    format: 'es'.transpile: false.env: 'production'.alias: { he: './entity-decoder' },
    banner
  },
Copy the code

Here we have found the main entry file for vue

Vue is really what

Enter the entry-Runtime-with-Compiler. js main file and analyze the trunk first, leaving out the branches:

// ...
import Vue from './runtime/index'
import { compileToFunctions } from './compiler/index'
// ...
Vue.prototype.$mount = function () {
 // ...
}
Vue.compile = compileToFunctions

export default Vue
Copy the code

runtime/index

We go to Runtime /index.js and continue looking for vue

import Vue from 'core/index'
// ...
export default Vue
Copy the code

core/index

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
// ...
initGlobalAPI(Vue)
// ...
export default Vue
Copy the code

instance/index

At this point, we finally see what Vue really is, which is really just a constructor.

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

So far, we have seen that vue. Js, once loaded, initializes five methods: initMixin, stateMixin, eventsMixin, lifecycleMixin, and renderMixin. Now, let’s break it down one by one.

Four initMixin.

 Vue.prototype._init = function (options? :Object) {
  const vm: Component = this
  // ...
}
Copy the code

Vue declares the _init method in this method. This is actually how we instantiate vUE when we use vUE, new vUE (options).

The core process of this method is as follows:

1. Merge the options configuration and mount it to vm.$options

2. initLifecycle

$parent, vm.$root, vm.$children, vm

3. initEvents

Initialize vm._events={}, the initialization event system is actually the parent component in the template using V-ON or @ registered to listen for events triggered within the child component

4. initRender

Two methods are mainly defined:

  • Vm. _c, which is used by the user to use the template pattern
  • Vm.$createElement, which is used by the user’s handwritten render function

Both of these methods will end up calling the createElement method, which will be discussed in the section on the virtual DOM. All we need to know is that this method returns the virtual DOM

5. Call the beforeCreate hook

6. initInjections

In this method, inject and provide come in pairs. The parent component provides data that can be received by children of any nested hierarchy. Provide is the data provider, inject is the data receiver.

7. initState

  • Initialize state, props, Methods, computed, and watch
  • When initializing state, props, and methods, vue traverses all keys in data to check whether the props, methods definitions are repeated
  • {a: {type: “xx”, default: ‘xx’}}
  • Mount both data and props to vm._data and vm._props. _data[xx], vm._props[xx]
  • Add responsive listeners to _data, _props

8. initProvide

With initInjections

9. Call the Created hook

Five stateMixin.

  • Prototype.$data, vue.prototype.$props
  • $data = vm._data; $props = vm._props
  • Defining the vue.prototype. $watch method actually instantiates the Watcher class

Six eventsMixin.

  • On the Vue prototype, define $ON, $once, $off, $emit event methods and return the VM

Seven lifecycleMixin.

  • Define the _update, $forceUpdate, $destroy methods on vue. prototype

Eight renderMixin.

  • On the Vue prototype, define the $nextTick method
  • On the Vue prototype, define the _render method. Note that this method calls vm.$createElement to create the virtual DOM. If the return value vNode is not of the virtual DOM type, an empty virtual DOM will be created.

The virtual DOM:VNode, which is actually a class, has the following structure:

class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; 
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void;
  parent: VNode | void; 

  / /... More properties, you can ignore them for a while
}
Copy the code

The createElement method finally creates a VNode with the following six parameters:

Parameter names instructions
context: Component Vue instance object
tag: any Tag name, which can be a String div or a component tag
data: VNodeData See interface VNodeData below
children: Array Nested divs and other structures
normalizationType: any Flag, 1 simple 2 always
alwaysNormalize: any Check whether always true is true

VNodeData data structure:

interface VNodeData { key? : string | number; slot? : string; scopedSlots? : { [key: string]: ScopedSlot |undefined}; ref? : string; refInFor? : boolean; tag? : string; staticClass? : string;class? :any; staticStyle? : { [key: string]: any }; style? : string | object[] | object; props? : { [key: string]: any }; attrs? : { [key: string]: any }; domProps? : { [key: string]: any }; hook? : { [key: string]:Function}; on? : { [key: string]:Function | Function[] };
  nativeOn?: { [key: string]: Function | Function[]}. transition? : object; show? : boolean; inlineTemplate? : {render: Function;
    staticRenderFns: Function[]; }; directives? : VNodeDirective[]; keepAlive? : boolean; }Copy the code

9. To summarize

  • Vue is essentially a constructor.
  • Initialize: state, props, methods, computed, watch, $destory, $data, $props, $forceUpdate, etc.
  • Add responsiveness to _data, _props using object.defineProperty
  • _data[xx], vm._props[xx]
  • By adding an event event system, you actually initialize events that the parent component registers in the template using V-ON or @ to listen for events that are triggered within the child component
  • Mount createElement in preparation for subsequent render returns the virtual DOM.
  • Call the corresponding component lifecycle hooks, beforeCreate, and Created

In the next chapter, we will analyze data change monitoring in VUE in detail

Code word is not easy, a lot of attention, like ~😽