Dong Yan is the front engineer of Wedoctor Cloud Service team. Aspiring to become a full stack development engineer or even architect, I have a long way to go. Exercise in your life to release stress and think about problems.
At present, Vue3.0 is very hot, there have been a lot of Vue3.0 source code analysis series of blogs, but Vue2.0 source CODE I think it is necessary to fine taste, master the original universal source code principle, in order to know the new version of Vue3.0 in the end what changes. If you are already familiar with it, skip ~
First take a look at the whole page rendering flow chart, along this picture we take further study with the problem, I believe that soon can overcome reading Vue source difficulties.
Initialize and mount
new Vue() -> $mount
Import Vue from ‘./instance/index’;
Then we navigate to the instance/index.js file and see import {initMixin} from ‘./init’
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); // This Vue parameter is defined in the current file
Copy the code
As you can see, in the Vue constructor above, this._init(options) is executed. This refers to the current Vue instance, defined from the initMixin() function.
We navigate to instance/init.js and see:
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
const vm: Component = this.// omit the middle processing
vm._self = vm
initLifecycle(vm) // Initialize the life cycle
initEvents(vm) // Initialize the event
initRender(vm) // Initialize render
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm) // Initialize props, methods, data, computed, and watch
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')...// omit part of the processing
if (vm.$options.el) {
vm.$mount(vm.$options.el) // Call $mount after initialization to mount the component}}}Copy the code
- After new Vue(), Vue calls the _init function to initialize, the init process here, which will:
- Initialize the life cycle:
initLifecycle(vm)
- Initialization event:
initEvents(vm)
- Initialize options like props, methods, data, computed, and Watch:
initState(vm)
- Initialize the life cycle:
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // props
if (opts.methods) initMethods(vm, opts.methods) // methods
if (opts.data) {
initData(vm) // data
} else {
observe(vm._data = {}, true)}if (opts.computed) initComputed(vm, opts.computed) // computed
if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch)// watch}}Copy the code
- The most important of these is setting setters and getters via Object.defineProperty for “reactive” and “dependency collection,” as I’ll explain later, but just to make an impression.
(Q1: How is Vue’s responsiveness and dependency collection implemented?)
-
Calling $mount after initialization will mount the component.
-
If you compile at run time, that is, if there is no render function but the template is used for rendering, the “compile” step is required.
(Q2: Vue template compilation process?)
compile
Compile can be divided into three stages: parse, optimize and generate.Check out compiler/index.js:
import { parse } from './parser/index'
import { optimize } from './optimizer'
import { generate } from './codegen/index'
import { createCompilerCreator } from './create-compiler'
// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
const ast = parse(template.trim(), options)
if(options.optimize ! = =false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
Copy the code
As you can see, a compiler is created.
- call
parse
Function generates an AST abstract syntax tree; - If the option optimize is true, it needs to be optimized and called
optimize
Functions; - Then, based on the generated AST, through the call
generate
Function generates snippet objects; - The final will be
AST
, code objectRender string
(Required for VNode rendering)
The staticRenderFns string in code is wrapped as an object.
parse
Parse parses data such as instructions, classes, and styles in the Template template using regexes to form the AST. (How to parse?)
optimize
Optimize is the ability to mark static nodes, which is an optimization of the Vue compilation process. The diff algorithm skips the static nodes when the interface is updated, reducing the comparison process. The patch performance is optimized.
Q3: How does Vue differentiate between static nodes? Vue patch process? What does the Diff algorithm do?
generate
Generate is the process of converting an AST into a Render function string. The result is the render string and the staticRenderFns string.
After going through the parse, Optimize, and generate phases, the component will have the Render function needed to render VNode.
Q4: What is a VNode?
responsive
When the Render function is rendered, it reads the value in the object and firesGetter functions for dependency collection
. The purpose of the dependency collection is to place the observer Watcher object into subs in the subscriber Dep.
When a value in an object is modified, the setter function is triggered to notify each Watcher in the previously collected Dep to rerender the view. Watcher, upon receiving the notification, calls the Update function to update the view. Of course, there is a patch process and the asynchronous update strategy using queues, which we will talk about later.
(Q5: Responsivity principle of Vue2.0?)
Virtual DOM
The virtual DOM is actually the product of the render Function implementation. It is a tree based on JavaScript objects (VNode nodes) and describes nodes with object attributes. In fact, it is just a layer of abstraction of the real DOM. Eventually, the tree can be mapped to the real world through a series of operations. Because Virtual DOM is based on JavaScript objects and does not depend on the real platform environment, it has the ability to cross platform, such as browser platform, Weex, Node, etc.
Update the view
-
When modifying an object value, the setter -> Watcher -> update process is used to modify the value of the object.
-
When the data changes, we perform the Render function to get a new VNode. The simplest and most crude way to get a new view is to parse the new VNode and render it into the real DOM using innerHTML. But we only changed a small part of it, which seemed wasteful.
-
Therefore, we can only modify the modified part, and then patch will be used for comparison. The new VNode and the old VNode are introduced into patch for comparison, and their “differences” are obtained through diff algorithm. Finally, we just need to modify the corresponding DOM of these “differences”.
conclusion
Back to the first picture:Do you have any idea how Vue works in general?
- Page rendering:
new Vue() -> init() -> $mount()
- Data Update:
Data needs to be updated due to user operations. The view needs to be updated -> Getter collects dependencies -> Dep.depend () -> dep.subs.push(watcher) -> setters notify watcher to update the view -> dep.notify() -> dep.subs[i].update() -> render fucntion() -> VNode -> patch -> DOM
- Template update:
Template compilation: parse -> optimize -> Generator
Some specific mechanism details, this series will be updated one by one, common learning, common progress! Some details may be misunderstood by the author. If you have any questions, please feel free to give feedback in the comments or comment area
The resources
- Analysis of vue. js Internal Operation Mechanism
- Vue2.0 source