[Vue2.0 Source Code Learning] Life Cycle - Template Compilation Phase

1 Introduction

In the previous articles, we introduced the initialization phase of the life cycle. We know that the vm.$mountmethod is called after all the work in the initialization phase is completed. The call of this method marks the end of the initialization phase and the entry into the next phase. From the official documentation, As you can see from the life cycle flow chart, the next stage is the template compilation stage. The main work done in this stage is to obtain the template content passed in by the user and compile it into a rendering function.

Insert image description here

The template compilation phase does not exist Vuein all builds, it only exists in the full version (ie vue.js. This stage does not exist in the runtime version (i.e. vue.runtime.js). This is because when using vue-loaderor vueify, *.vuethe template inside the file will be pre-compiled into a rendering function at build time, so compilation is not required, so there is no template compilation. stage, from the initialization stage of the previous step directly to the mounting stage of the next stage.

Here, we need to introduce what is the full version and the runtime version only.

vueThere are two versions built based on the source code, one runtime only(a version containing only the runtime) and the other runtime + compiler(a complete version containing both the compiler and the runtime). The only difference between the two versions is that the latter includes a compiler.

  • complete version

    A complete Vueversion includes a compiler and we can use templateoptions for template writing. The compiler will automatically templatecompile the template string in the option into the code of the rendering function, which is renderthe function in the source code. If you need to compile the template client-side (such as passing a string to an templateoption, or mounting it on an element and using its DOMinner HTML as a template), you will need a version that includes the compiler. as follows:

    // 需要编译器的版本
    new Vue({
          
          
      template: '<div>{
          
          { hi }}</div>'
    })
    
  • Contains only runtime version

    The runtime-only version has functions such as creating Vueinstances, rendering and processing Virtual DOM, and is basically the complete code except the compiler. There are two applicable scenarios for this version:

    1. We define the rendering process through handwritten functions in the options render. At this time, we do not need to include the compiler version for complete execution.

    // 不需要编译器
    new Vue({
          
          
      render (h) {
          
          
        return h('div', this.hi)
      }
    })
    

    2. Compile with the help vue-loaderof such compilation tools. When we use webpackengineering Vuedevelopment, we often compile files. Although we also use template tags to write code, there is no vue-loaderneed to use the compiler to take responsibility at this time. The compilation of the template is done, and this process is left to the plug-in to implement.*.vuetemplateVue

Obviously, the compilation process will cause a certain loss in performance, and due to the addition of compiled process code, the Vuetotal size of the code is also larger (the runtime version is about 30% smaller than the full version). Therefore, in actual development, we need to compile with tools like this and webpackmerge the compilation phase of the template into the build process. This not only reduces the size of the production environment code, but also greatly improves the runtime performance, killing two birds with one stone. .vue-loaderVuewebpack

In order to learn the source code completely, this article will analyze what is done in the template compilation phase in the full version.

2. Analysis of template compilation stage

As mentioned above, the difference between the full version and the runtime-only version mainly lies in whether there is a template compilation stage, and whether there is a template compilation stage is mainly reflected in the implementation of the method vm.$mount. At this point you may have questions: So, $mountthere are two versions of the method? Yes, you can understand it that way, but in the final analysis it is still the same. Let's take a look at each.

2.1 Comparison of two $mount methods

The code containing only the runtime version $mountis as follows:

Vue.prototype.$mount = function (el,hydrating) {
    
    
  el = el && inBrowser ? query(el) : undefined;
  return mountComponent(this, el, hydrating)
};

$mountAfter obtaining the element elcorresponding to the option within the method in this version, the function DOMis directly called mountComponentto perform the mounting operation. We will introduce this function in detail in the mounting phase.

The complete version of $mountthe code is as follows:

var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el,hydrating) {
    
    
  // 省略获取模板及编译代码

  return mount.call(this, el, hydrating)
}

Note that $mountbefore the definition of the complete version, the methods Vueon the prototype $mountare cached and recorded as variables mount. At this point you may ask, this $mountmethod has not been defined yet, how can it be cached in the first place.

In fact, in the source code, $mountthe method that contains only the runtime version is first defined, and then the complete version of the $mountmethod is defined, so the cached mountvariable at this time is the method that only contains the runtime version $mount.

Why do you do that? As we said above, the difference between the full version and the runtime-only version mainly lies in whether there is a template compilation phase. The runtime-only version has no template compilation phase. After the initialization phase is completed, it directly enters the mounting phase, while the full version is After the initialization phase is completed, it enters the template compilation phase, and then enters the mounting phase. In other words, both versions will eventually enter the mounting stage. $mountTherefore , after compiling the template in the full version method, you need to go back and call $mountthe method that only contains the runtime version to enter the mounting phase.

This means that in the full version of $mountthe method, the method that only contains the runtime version $mountis cached and recorded as a variable mount, and then mountthe method is executed after the template is compiled (that is, the method that only contains the runtime version $mount).

Therefore, the analysis template compilation stage is actually vm.$mountthe implementation of the method of analyzing the complete version.

2.2 Complete version of vm.$mount method analysis

The complete version of vm.$mountthe method definition is located in the source code src/platforms/web/entry-runtime-with-compiler.js, as follows:

var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el,hydrating) {
    
    
  el = el && query(el);
  if (el === document.body || el === document.documentElement) {
    
    
    warn(
      "Do not mount Vue to <html> or <body> - mount to normal elements instead."
    );
    return this
  }

  var options = this.$options;
  // resolve template/el and convert to render function
  if (!options.render) {
    
    
    var template = options.template;
    if (template) {
    
    
      if (typeof template === 'string') {
    
    
          if (template.charAt(0) === '#') {
    
    
            template = idToTemplate(template);
            /* istanbul ignore if */
            if (!template) {
    
    
              warn(
                ("Template element not found or is empty: " + (options.template)),
                this
              );
            }
          }
      } else if (template.nodeType) {
    
    
        template = template.innerHTML;
      } else {
    
    
        {
    
    
          warn('invalid template option:' + template, this);
        }
        return this
      }
    } else if (el) {
    
    
      template = getOuterHTML(el);
    }
    if (template) {
    
    
      if (config.performance && mark) {
    
    
        mark('compile');
      }

      var ref = compileToFunctions(template, {
    
    
        outputSourceRange: "development" !== 'production',
        shouldDecodeNewlines: shouldDecodeNewlines,
        shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this);
      var render = ref.render;
      var staticRenderFns = ref.staticRenderFns;
      options.render = render;
      options.staticRenderFns = staticRenderFns;

      if (config.performance && mark) {
    
    
        mark('compile end');
        measure(("vue " + (this._name) + " compile"), 'compile', 'compile end');
      }
    }
  }
  return mount.call(this, el, hydrating)
};

As you can see from the code, the function can be roughly divided into three parts:

  • elGet elements based on the parameters passed in DOM;
  • Get the incoming template without the user's handwritten renderfunction template;
  • Compile the obtained information templateinto rendera function;

Next we will analyze them one by one.

elFirst, get the element based on the parameters passed in DOM. as follows:

el = el && query(el);

function query (el) {
    
    
  if (typeof el === 'string') {
    
    
    var selected = document.querySelector(el);
    if (!selected) {
    
    
      warn(
        'Cannot find element: ' + el
      );
      return document.createElement('div')
    }
    return selected
  } else {
    
    
    return el
  }
}

Since elthe parameter can be an element or a string type element selector, querythe function is called to obtain the elcorresponding DOMelement. Since the function is relatively simple, it obtains the corresponding elements in different ways querydepending on whether the passed parameter is a string , so we will not introduce it line by line here.elDOM

elIn addition, there is an additional judgment here, that is, if the corresponding DOMelement is obtained bodyor htmlelement, a warning will be thrown. This is because Vuethe content in the template will be replaced with the elcorresponding DOMelement. If it is bodyan or element, the entire document htmlwill be destroyed after replacement , so it is not allowed to be or . as follows:DOMelbodyhtml

if (el === document.body || el === document.documentElement) {
    
    
  warn(
    "Do not mount Vue to <html> or <body> - mount to normal elements instead."
  );
  return this
}

Then, renderget the incoming template if the user does not have a handwritten function template; as follows:

if (!options.render) {
    
    
  var template = options.template;
  if (template) {
    
    
    if (typeof template === 'string') {
    
    
      if (template.charAt(0) === '#') {
    
    
        template = idToTemplate(template);
        /* istanbul ignore if */
        if (!template) {
    
    
          warn(
            ("Template element not found or is empty: " + (options.template)),
            this
          );
        }
      }
    } else if (template.nodeType) {
    
    
        template = template.innerHTML;
    } else {
    
    
      {
    
    
        warn('invalid template option:' + template, this);
      }
      return this
    }
  } else if (el) {
    
    
    template = getOuterHTML(el);
  }
}

templateFirst, the options passed in by the user are obtained and assigned to the variable template. If the variable templateexists, then it is judged that if templateit is a string and ##starts with, it is considered templateto be ida selector, and the function is called to obtain the element idToTemplatecorresponding to the selector as a template, as follows:DOMinnerHTML

if (template) {
    
    
  if (typeof template === 'string') {
    
    
    if (template.charAt(0) === '#') {
    
    
      template = idToTemplate(template);
    }
  }
}

var idToTemplate = cached(function (id) {
    
    
  var el = query(id);
  return el && el.innerHTML
});

If templateit is not a string, then determine whether it is an DOMelement. If it is, use the DOMelement innerHTMLas a template, as follows:

if (template.nodeType) {
    
    
  template = template.innerHTML;
}

If it is neither a string nor DOMan element, a warning will be thrown: prompting the user that templatethe option is invalid. as follows:

else {
    
    
  {
    
    
    warn('invalid template option:' + template, this);
  }
  return this
}

If the variable templatedoes not exist, it means that the user has not passed in an option, and the function is called to obtain the external template templateaccording to the passed elparameters , as follows:getOuterHTML

if (el) {
    
    
  template = getOuterHTML(el);
}

function getOuterHTML (el) {
    
    
  if (el.outerHTML) {
    
    
    return el.outerHTML
  } else {
    
    
    var container = document.createElement('div');
    container.appendChild(el.cloneNode(true));
    return container.innerHTML
  }
}

Whether templatethe template is obtained from internal options or externally, the template content passed in by the user must be obtained. With the template content, the template can be compiled into a rendering function.

After obtaining the template, the next thing to do is to compile it into a rendering function, as follows:

if (template) {
    
    
  var ref = compileToFunctions(template, {
    
    
    outputSourceRange: "development" !== 'production',
    shouldDecodeNewlines: shouldDecodeNewlines,
    shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
    delimiters: options.delimiters,
    comments: options.comments
  }, this);
  var render = ref.render;
  var staticRenderFns = ref.staticRenderFns;
  options.render = render;
  options.staticRenderFns = staticRenderFns;
}

The specific steps of compiling templates into rendering functions have been introduced in detail in the previous article Template Compilation. Here, we will only briefly review them.

Compiling the template into a rendering function is compileToFunctionsperformed in a function. This function receives the template string to be compiled and compilation options as parameters, returns an object, and the renderproperties in the object are the compiled rendering functions. Finally, the rendering function is set to $optionssuperior.

3. Summary

This article introduces the second stage in the life cycle - the template compilation stage.

First, two versions of the source code build are introduced Vue: the full version and the runtime-only version. And we know that the template compilation phase only exists in the full version, and does not exist in the runtime-only version. This is because in the runtime-only version, when using or, the template inside the file will vue-loaderbe vueifybuilt *.vuewhen It is precompiled into a rendering function, so no compilation is required, so there is no template compilation stage.

$mountThen the differences between the two version methods were compared . The difference is $mountwhether template compilation is done in the method. In the method that only contains the runtime version, the element is $mountobtained DOMand directly enters the mounting phase. In the complete version, $mountthe template is compiled first, and then $mountthe method that contains only the runtime version is called back to enter the mounting phase.

Finally, we learned that the analysis template compilation stage is actually the analysis of the implementation of the full version of the method. We analyzed vm.$mountthe full version of the method source code line by line. vm.$mountWe know that the work done in this stage is to obtain the internal or external template passed in by the user from elthe options and options passed in by the user, and then compile the obtained template into a rendering function.template

Guess you like

Origin blog.csdn.net/weixin_46862327/article/details/132962561
Recommended