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

  1. .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
  2. 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)

  1. Initialize the various properties and methods first
  2. .html file mode:
    1. Get the dom code string template for #app
    2. Compiling the template generates the AST tree and the anonymous rendering function
  3. .vue file mode:
    1. Vue-loader compiles. Vue files to get anonymous rendering functions
  4. Implement vm. _update (vm) and _render (), hydrating); // (key function) render/update function
    1. First execute vm._render() and get virtual DOM tree vnode by performing anonymous render function
    2. 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

  1. 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
  2. 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 >”

  3. 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

  4. callHook(vm, ‘beforeMount’); / / beforeMount execution

  5. Implement vm. _update (vm) and _render (), hydrating); // (key function) render/update function

    1. 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

    2. 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…

  6. callHook(vm, ‘mounted’); // Mounted to retrieve the DOM element

  7. 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

  1. Initialize various properties and methods first (same as.html file mode)

  2. Through vue-loader compile. Vue file, get the anonymous rendering function

    1. 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
    1. Summary: Vue-loader is used to compile. Vue files and get anonymous rendering functions
  3. 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!