Vue source code parsing process – including two modes (and vue-loader)
Since the last internal sharing session to share the Vue diff algorithm, partners are unanimously interested in vue source code, that’s the whole
Note: The following content only describes a few key steps. Personally, I think I should first understand the key steps, and after knowing the input and output of the key steps, I can also try to achieve it by hand
2 modes refers to
- .html file mode. .html file using vue, no vue-loader
- The.html file is the basic implementation of vUE and does not need to be added to vue-loader. Understand this process first, then understand better what vue-loader does
- Vue file mode. Using webpack project, use vue-loader to parse. Vue files
The build process
(2 modes, most of the process is the same, on the acquisition of anonymous render function is different)
- Initialize the various properties and methods first
- .html file mode:
- Get the dom code string template for #app
- Compiling the template generates the AST tree and the anonymous rendering function
- .vue file mode:
- Vue-loader compiles. Vue files to get anonymous rendering functions
- Implement vm. _update (vm) and _render (), hydrating); // (key function) render/update function
- First execute vm._render() and get virtual DOM tree vnode by performing anonymous render function
- After executing vm._update(), layer upon layer recursively the virtual DOM tree vNode to get the real DOM node, then update to the real DOM tree, and then the browser renders the latest DOM tree
.html file mode compilation process
The.html file is the basic implementation of vUE and does not need to be added to vue-loader. Understand this process first, then understand better what vue-loader does
Test file:.html file
- CDN introduces an uncompressed version of VUE, which is used directly within script tags
<! DOCTYPEhtml> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> {{aa}} --- 1 <div @click="qqq">click me</div> {{C_aa}} </div> <script type="module"> debugger new Vue({ el: '#app'.data: { aa: 123 }, watch: { aa (nval, oval) { console.log(nval, oval) } }, computed: { C_aa () { return this.aa + 100}},methods: { async qqq () { this.aa = this.aa + 1}}})</script> </body> </html> Copy the code
The following is executed in source code order, from top to bottom
-
Initialize the various properties and methods first
initLifecycle(vm); // Initialize the life cycle initEvents(vm); // Initialize the event initRender(vm); // Initialize the function that handles the rendering template callHook(vm, 'beforeCreate'); / / beforeCreate execution initInjections(vm); // Initialize inject before data/props initState(vm); / / initialize the props, the methods, data, computed, watch initProvide(vm); // Initialize provide after data/props callHook(vm, 'created'); / / created Copy the code
-
Get the dom code string for #app
template = getOuterHTML(el);
Print the template is: “< div id =” app “> {{aa}} – 1 < div @ click =” : QQQ “> click me < / div > {{C_aa}} < / div >”
-
Compiling the template generates the AST tree and the anonymous rendering function
var compiled = compile(template, options);
-
Get an AST tree (the benefit is that it is easy to parse some of the vUE’s specific syntax inside the tag, which ultimately helps to generate anonymous rendering functions)
compiled: { ast: 太大了 如图, } Copy the code
Tree structure, all the children are inside children
-
Get the anonymous render function
compiled: { render: with(this){return _c('div', {attrs: {"id":"app"}},[_v("\n "+_s(aa)+" --- 1\n "),_c('div', {on: {"click":qqq}},[_v("click me")]),_v("\n "+_s(C_aa)+"\n")]}}Copy the code
Through createFunction (compiled. Render, fnGenErrors); Get the anonymous render function
(function anonymous( ) { with(this){return _c('div', {attrs: {"id":"app"}},[_v("\n "+_s(aa)+" --- 1\n "),_c('div', {on: {"click":qqq}},[_v("click me")]),_v("\n "+_s(C_aa)+"\n")]}})Copy the code
It also caches the result of the anonymous render function, where key is the template above and value is the anonymous render function
-
-
callHook(vm, ‘beforeMount’); / / beforeMount execution
-
Implement vm. _update (vm) and _render (), hydrating); // (key function) render/update function
- First execute vm._render() by executingAnonymous rendering function, the virtual DOM tree vnode is obtained
- Virtual DOM tree VNode uses JS objects to represent the DOM tree
- Working with JS is much better than working with the real DOM, especially when it comes to diff algorithms between old and new VNodes
// Execute the anonymous render function to get the vNode virtual DOM treevnode = render.call(vm._renderProxy, vm.$createElement); The following figure shows the vNode structureCopy the code
- After executing vm._update(), layer upon layer recursively the virtual DOM tree vNode to get the real DOM node, then update to the real DOM tree, and then the browser renders the latest DOM tree
if(! prevVnode) {// First render /* The first render, inside which the real DOM is generated recursively, layer by layer of createElement, in appendChild to the page, in the dom */ of removeChild #app vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */); } else { /* Updates the old vonde and the new vonde using the diff algorithm, layer by layer (time complexity is n), to efficiently get the difference between the old and new VonDe, and then generate the real DOM based on the vNode, and update the real DOM tree */ vm.$el = vm.__patch__(prevVnode, vnode); // Old vNode and new vNode } Copy the code
For details on the Diff algorithm, see my other article:Juejin. Cn/post / 685003…
- First execute vm._render() by executingAnonymous rendering function, the virtual DOM tree vnode is obtained
-
callHook(vm, ‘mounted’); // Mounted to retrieve the DOM element
-
End of the call
.vue file mode compilation process
Test file:The vue file(Same content as above)
-
Using webpack project, use vue-loader to parse. Vue files
app.vue
<template> <div id="app"> {{aa}} --- 1 <div @click="qqq">click me</div> {{C_aa}} </div> </template> <script> export default { name: 'App', data () { return { aa: 123}},watch: { aa (nval, oval) { console.log(nval, oval) } }, computed: { C_aa () { return this.aa + 100}},methods: { async qqq () { this.aa = this.aa + 1}}}</script> Copy the code
main.js
import Vue from 'vue' import App from './App.vue' console.log(App) debugger new Vue({ render: h= > { debugger console.log(h(App)) return h(App) } }).$mount('#app') Copy the code
The following is executed in source code order, from top to bottom
-
Initialize various properties and methods first (same as.html file mode)
-
Through vue-loader compile. Vue file, get the anonymous rendering function
- Let’s take a look at what vue-loader’s compiled.vue file looks like :(printed at line 3 of main.js above)
2. Click on the App. Vue? After 6fd5:1, we can get the followingAnonymous rendering function
var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c("div", { attrs: { id: "app" } }, [ _vm._v("" + _vm._s(_vm.aa) + "- 1"), _c("div", { on: { click: _vm.qqq } }, [_vm._v("click me")]), _vm._v("" + _vm._s(_vm.C_aa) + "")])}var staticRenderFns = [] render._withStripped = true export { render, staticRenderFns } Copy the code
- Summary: Vue-loader is used to compile. Vue files and get anonymous rendering functions
-
The rest of the steps below are the same as the.html file pattern
The next article
Scheduling Principle of VUE source Code Parsing (Responsive Principle)
Code word is not easy, praise encouragement!