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 ~😽