1. Start with vUE packaging

Vue package version description

| | UMD | CommonJS | ES Module |

| — | — | — | — |

| Full | vue.js | vue.common.js | vue.esm.js |

| Runtime-only | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |

| Full (production) | vue.min.js | | |

| Runtime-only (production) | vue.runtime.min.js | | |

Vue source code compilation files are functionally divided into two main types: Full version and Runtime-only version.

The Full version includes a compiler that converts template strings into JS render functions, and runtime code that generates VUE instances, renders and inserts virtual DOM, and so on.

The run-time only version is 30% smaller than the Full version. The separation of compile time and run time also improves performance.

The Run-time only version is therefore preferred for performance. To use runtime-only versions, the best practice is to develop as a single-file component and complete the compilation ahead of time.

2. Single-file components

Single-file components (SFC) in Vue are files with the extension name. Vue, as shown in the following example:


<template>

  <div>

    <p>{{msg}},world</p>

  </div>

</template>

<script>

export default {

  data(){

    return {

      msg: 'hello'

    }

  }

}

</script>

<style lang="scss" scoped>

p {

  color: red;

}

</style>

Copy the code

This is the kind of single-file component that we typically use in our development projects. A single-file component can contain four parts: template, script, styles, and customBlocks.

So how does an SFC become code that can be executed in a browser? This comes down to compiling single-file components.

3. Compilation of single-file components

In a typical project, we would compile single-file components using webpack+vue-loader.

Start with a simple WebPack configuration file:


const VueLoaderPlugin = require("vue-loader/lib/plugin");

module.exports = {

  module: {

    rules: [

      {

        test: /.vue$/,

        use: [{ loader: "vue-loader" }],

      }

    ],

  },

  plugins: [

    new VueLoaderPlugin()

  ],

};

Copy the code

It can be seen that a plugin needs to be configured when vue-loader is configured.

In order to develop the following content reasonably, we need to know some pre-knowledge of Webpack:

1. What is the difference between Webpack loader and Plugin? Loader is A converter that can compile A file into B file. It is mainly used to convert non-JS modules into JS modules. For example, less is converted into CSS by less-loader, and CSS references are processed by CSS-loader. Finally, the STYle-loader is used to convert the CSS to the js file loaded by the script. Plugin, on the other hand, is an extender that listens on certain nodes in the WebPack packaging process to perform a broader set of tasks. It is powerful enough to handle a wide variety of tasks, from performance optimization to code compression, from defining environment variables to rewriting HTML files.


2. What is the execution sequence of loader in Webpack? In Webpack, loaders can be classified into four types: Pre, POST, normal and inline. The sequence of loader calls is pre > Normal > Inline > POST. In the case of the same type of loader, the priority of invocation is bottom-up, right-to-left, and vice versa in the case of pitch.

After the pre-knowledge preview, let’s take a look at the overall SFC compilation process. The whole process can be divided into two stages:

1. Plugin execution Stage This stage is actually the VueLoaderPlugin code execution process in the webPack configuration file above. The only core task of VueLoaderPlugin is to restructure rules, including adding pitcher Loader and clone of existing rules into webPack configuration information module. Rules. Vue-loader has some complicated things that it doesn’t want users to worry about, so it automatically implements them in the plugin phase.

2. Loader execution stage Since pitcher Loader and Clone rules were dynamically injected in the Plugin stage, the Loader stage mainly includes: a. The SFC file calls Vue-Loader to generate the intermediate result Webpack module; B. The newly generated Webpack Module matches resourceQuery rule and calls pitcher Loader. According to different processing logic, the new intermediate result Webpack Module will be generated. C. The new WebPack Module generated again matches the specific loader processed by Clone in the Plugin stage, and directly calls the specific Loader for processing.

Let’s look at each process in detail.

Look at the vue – loader. For a loader, the input parameter is the result or resource file generated by the previous loader, in this case the.vue file.

Once you have the contents of the file, the first step is to parse it.

Start parsing THE SFC, which is actually disassembly the corresponding contents according to different blocks. Parsing function is provided by @vue/component-compiler-utils and VUe-template-Compiler.

The single-file component in the example above will be resolved into the following objects:

{ template: { type: 'template', content: '\n<div>\n<p>{{a}},world</p>\n</div>\n', start: 21, end: 62 }, script: { type: 'script', content: '//\n//\n//\n//\n//\n\nexport default {\n data () {\n return {\n msg: \'hello\'\n }\n }\n}\n', start: 83, attrs: {}, end: 158, map: { version: 3, sources: [Array], names: [], mappings: ';;;;;; AAMA; AACA; AACA; AACA; AACA; AACA; AACA', file: 'source.vue', sourceRoot: 'example', sourcesContent: [Array] } }, styles: [ { type: 'style', content: '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\np {\n color: red;\n}\n', start: 183, attrs: [Object], module: true, end: 207, map: [Object] } ], errors: [] }Copy the code

Continue processing the parsed objects generated by the transformation,

The template module generates module import statements:

import { render, staticRenderFns } from "./source.vue? vue&type=template&id=27e4e96e&Copy the code

The script module generates module import statements:

import script from "./source.vue? vue&type=script&lang=js&" export * from "./source.vue? vue&type=script&lang=js&"Copy the code

The styles module generates module import statements:

import style0 from "./source.vue? vue&type=style&index=0&module=true&lang=css&"Copy the code

As you can see in the generated code, there is a new round of vue module imports, but each import has a parameter section. For example, the template module generates the module import statement containing the ‘? Vue&type = template&id = 27 e4e96e ‘parameters.

During the import process, according to the execution sequence of Webpack Loader in the preceding knowledge, the pitcher loader with pitch attribute will be executed first. The pitcher Loader matches the import of these new VUE modules exactly according to whether there is’ VUE ‘in resourceQuery.

The pitcher Loader is built to intercept the vUE module requests generated here. After the pitcher Loader intercepted the Vue module, it found two modules, Template and Style, and inserted the templateLoader and stylePostLoader that came with vuE-Loader respectively.

The main function of templateLoader is to compile the template file into the render function. See previous article for the template compilation process: Do you really understand vue template compilation? .

StylePostLoader is mainly used to process scope CSS.

After being processed by pitcher Loader, module parsing will find the Vue Loader source code to walk the logic again. But this time I’m going to go into the selectBlock module and do it. Source code in vue-loader select.js file:

if (query.type === `template`) { if (appendExtension) { loaderContext.resourcePath += '.' + (descriptor.template.lang ||  'html') } loaderContext.callback( null, descriptor.template.content, descriptor.template.map ) return } // script if (query.type === `script`) { if (appendExtension) { loaderContext.resourcePath += '.' + (descriptor.script.lang || 'js') } loaderContext.callback( null, descriptor.script.content, descriptor.script.map ) return } // styles if (query.type === `style` && query.index ! = null) { const style = descriptor.styles[query.index] if (appendExtension) { loaderContext.resourcePath += '.' + (style.lang || 'css') } loaderContext.callback( null, style.content, style.map ) return } // custom if (query.type === 'custom' && query.index ! = null) { const block = descriptor.customBlocks[query.index] loaderContext.callback( null, block.content, block.map ) return }Copy the code

As you can see, the file according to the different template/script/style/custom module to deal with its corresponding content. Finally, a Webpack-configured loader is used to rule match the corresponding suffix and process the corresponding content returned by loaderContext.callback.

The compilation of vue Loader is complete. So why does packaging take so long, because WebPack imports and exports in circles between various loaders.

4. Compilation principle

High-level languages inevitably involve language processors, which need to be translated from human-oriented high-level languages into machine-oriented operating languages.

The Java language, for example, generates bytecode through the compiler and executes it through the virtual machine’s interpreter. In fact, the SFC compilation process described above is the same. Webpack is used to compile vUE single-file components that are more suitable for development into a scripting language that the browser can run. Abstract syntax trees are more or less used in each step of the process.

In the world of front-end compilation, abstract syntax trees are the most widely used. The specification comes from the ESTree project. The project was originally designed to ensure consistency with the ES specification through the power of the community, and express the AST of JavaScript with a custom syntax structure. Later, as the popularity grew and several well-known engineers joined in, it became a de facto specification. Mozilla and the community are currently maintaining the library.

Applications of abstract syntax trees include but are not limited to:

  • Babel: implement JS compilation, conversion process is AST conversion
  • ESlint: Code error or style check to find potential errors
  • IDE error prompts, formatting, highlighting, autocomplete, etc
  • UglifyJS compression code
  • Code packaging tool Webpack

With the rapid pace of front-end engineering, front-end compilation is becoming an essential skill. The above is just the tip of the iceberg in the field of compilation, more knowledge needs to be developed and mined.