The background,

Import XXX from ‘xxxx.vue’ is one of the most common syntax in vue projects, but it’s probably not thought about. What happens behind the scenes when you import a vue file.

Today we’ll take a look at the process behind importing a vue file.

2. Webpack loader mechanism

We know that native import cannot import vue files, so why do we use it in our code?

This is because webPack’s Loader mechanism, by configuring the loader for the file type, can use the specified loader to process the file once before handing it over to the native import syntax. So the loader for our vue file is vue-loader.

We can confirm this with the configuration of WebPack:

// WebPack configuration in the project
module: {
    rules: [
      // If a vue file is matched, use the Vue-Loader to process it
      {
        test: /\.vue$/.loader: 'vue-loader'.options: vueLoaderConfig
      }]
}
Copy the code

Vue-loader processes vUE files

It is already known that the Vue file will be processed by vue-Loader when it is imported, so how does the Vue-Loader process it?

Here, let’s first take a look at what the vue-Loader has done.

In order to see the vue-Loader processing results, we need to modify the Vue-Loader code in node_modules and print the processing results:

/ / the vue - loader - 13.7.3 version
// vue-loader\lib\loader.js
  // Line 447, add console.log, print output
  console.log('vue-loader output =>',output)
  return output
Copy the code

Once the changes are complete, you need to restart the project and see the printed results in the vscode console

// vue-loader processes the result of import App from 'app.vue'
function injectStyle (ssrContext) {
    if (disposed) return
    require("!!!!! vue-style-loader! css-loader? {\"sourceMap\":true}! . /node_modules/vue-loader/lib/style-compiler/index? {\"vue\":true,\"id\":\"data-v-7ba5bd90\",\"scoped\":false,\"hasInlineConfig\":false}! . /node_modules/vue-loader/lib/selector? type=styles&index=0! ./App.vue")}var normalizeComponent = require(! "" . /node_modules/vue-loader/lib/component-normalizer")
/* script */
export * from "!!!!! babel-loader! . /node_modules/vue-loader/lib/selector? type=script&index=0! ./App.vue"
import __vue_script__ from "!!!!! babel-loader! . /node_modules/vue-loader/lib/selector? type=script&index=0! ./App.vue"
/* template */
import __vue_template__ from "!!!!! . /node_modules/vue-loader/lib/template-compiler/index? {\"id\":\"data-v-7ba5bd90\",\"hasScoped\":false,\"transformToRequire\":{\"video\":[\"src\",\"poster\"],\"source\":\"src\ ",\"img\":\"src\",\"image\":\"xlink:href\"},\"buble\":{\"transforms\":{}}}! . /node_modules/vue-loader/lib/selector? type=template&index=0! ./App.vue"
/* template functional */
var __vue_template_functional__ = false
/* styles */
var __vue_styles__ = injectStyle
/* scopeId */
var __vue_scopeId__ = null
/* moduleIdentifier (server only) */
var __vue_module_identifier__ = null
var Component = normalizeComponent(
    __vue_script__,
    __vue_template__,
    __vue_template_functional__,
    __vue_styles__,
    __vue_scopeId__,
    __vue_module_identifier__
)
Component.options.__file = "src/App.vue"

export default Component.exports
Copy the code

As you can see, the output of vue-Loader is a js code that will be executed by native import.

By looking at this js code, we can roughly see: First, the three parts of the vue file are processed separately, namely, the template part, the Script part and the style part, and then the normalizeComponent method in component-Normalizer.js is used for summary processing.

Let’s first look at how the three parts of the vue file are handled.

3.1 template section

First look at the processing code in the Template section

// Vue-loader outputs code that handles the template part of the output
import __vue_template__ from "!!!!! . /node_modules/vue-loader/lib/template-compiler/index? {\"id\":\"data-v-7ba5bd90\",\"hasScoped\":false,\"transformToRequire\":{\"video\":[\"src\",\"poster\"],\"source\":\"src\ ",\"img\":\"src\",\"image\":\"xlink:href\"},\"buble\":{\"transforms\":{}}}! . /node_modules/vue-loader/lib/selector? type=template&index=0! ./App.vue"
Copy the code

This line of code looks a bit long, so you can parse it in two pieces:

// The first paragraph!! . /node_modules/vue-loader/lib/template-compiler/index? {\"id\":\"data-v-7ba5bd90\",\"hasScoped\":false,\"transformToRequire\":{\"video\":[\"src\",\"poster\"],\"source\":\"src\ ", \ "img \" : \ "SRC \" and \ "image \" : \ "xlink: href \ buble"}, \ "\" : {\ "transforms \" : {}}} / / the second paragraph! . /node_modules/vue-loader/lib/selector? type=template&index=0! ./App.vueCopy the code

The second code uses vue-loader/lib/selector. Js to process the app.vue file. What does this selector do

/ / the vue - loader/lib/selector. Js annotation

// this is a utility loader that takes a *.vue file, parses it and returns
// the requested language block, e.g. the content inside <template>, for
// further processing.
Copy the code

The comment in selectors. Js indicates that this method is used to read the contents of the corresponding tag from the vue file, so the result of the second code is to read the code from the template tag in app.vue.

If you look at the first section of code, it still looks a little long, but you can see that it calls vue-loader/lib/template-compiler/index.js, and the rest of the code is the parameters to be processed.

The vue-loader/lib/template-compiler/index.js package is used to compile the template. The vue-template-compiler package is used to compile the template. The vue-template-compiler is generated from the Vue source code, so the template compilation in the VUe-Loader is consistent with the template compilation in the Vue source code. After the template is compiled, the output is the render function. This can be confirmed by printing the results:

// vue-loader processes the template part of app. vue output
var render = function() {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c("div", { attrs: { id: "app" } }, [_c("HelloWorld")].1)}var staticRenderFns = []
render._withStripped = true
var esExports = { render: render, staticRenderFns: staticRenderFns }
export default esExports
if (module.hot) {
  module.hot.accept()
  if (module.hot.data) {
    require("vue-hot-reload-api")      .rerender("data-v-7ba5bd90", esExports)
  }
}
Copy the code

3.2: Script part

Next, look at the processing in the script section:

// import __vue_script__ from "!! babel-loader! . /node_modules/vue-loader/lib/selector? type=script&index=0! ./App.vueCopy the code

This line of code looks short, but it also has two segments

/ / the first paragraph!!!!! babel-loader/ / the second paragraph! . /node_modules/vue-loader/lib/selector? type=script&index=0! ./App.vueCopy the code

The second section is the same as in template, except that the script part of the code is taken

The first is to use babel-Loader to process the part of the script that is fetched

The script part is very clear. You Babel the code inside the script tag in the vue file. You can see the result by breaking the point in component-Normalizer.js

3.3: Style section

Let’s move on to the style section

// Vue-loader outputs the code to handle the style part of the result
function injectStyle (ssrContext) {
    if (disposed) return
    require("!!!!! vue-style-loader! css-loader? {\"sourceMap\":true}! . /node_modules/vue-loader/lib/style-compiler/index? {\"vue\":true,\"id\":\"data-v-7ba5bd90\",\"scoped\":false,\"hasInlineConfig\":false}! . /node_modules/vue-loader/lib/selector? type=styles&index=0! ./App.vue")}Copy the code

Unlike template and script, the style section defines a handler injectStyle, in which the style section is handled, and the call timing of the function is analyzed later

So let’s look at how the code inside the style tag is handled in the function, divided into three sections, okay

// The first paragraph!! Vue-style-loader // Second paragraph! css-loader? {\"sourceMap\":true}! . /node_modules/vue-loader/lib/style-compiler/index? {\ "vue \" : true, \ "id \" : \ "data - v - 7 ba5bd90 \", \ "scoped \" : false, \ "hasInlineConfig \" : false} / / the third paragraph! . /node_modules/vue-loader/lib/selector? type=styles&index=0! ./App.vue"Copy the code

The third paragraph is the same as in template, except that the code is in the style section

The second section uses the CSS-Loader to process the style part of the code

The first step is to use the Ue – style-Loader to process the code that has been processed by the CSS-Loader

3.4: normalizeComponent function processing

After analyzing the vue- Loader’s processing of the three parts of the vue file, normalizeComponent is equivalent to a comprehensive processing of the three parts.

// Vue -loader source lib\ Component - Normalizer.js
module.exports = function normalizeComponent (
  rawScriptExports,
  compiledTemplate,
  functionalTemplate,
  injectStyles,
  scopeId,
  moduleIdentifier /* server only */
) {
  var esModule
  var scriptExports = rawScriptExports = rawScriptExports || {}

  // ES6 modules interop
  var type = typeof rawScriptExports.default
  if (type === 'object' || type === 'function') {
    esModule = rawScriptExports
    scriptExports = rawScriptExports.default
  }

  // Vue.extend constructor export interop
  var options = typeof scriptExports === 'function'
    ? scriptExports.options
    : scriptExports

  // render functions
  if(compiledTemplate) { options.render = compiledTemplate.render options.staticRenderFns = compiledTemplate.staticRenderFns  options._compiled =true
  }

  // functional template
  if (functionalTemplate) {
    options.functional = true
  }

  // scopedId
  if (scopeId) {
    options._scopeId = scopeId
  }

  var hook
  if (moduleIdentifier) { // server build
    hook = function (context) {
      / / 2.3 injection
      context =
        context || // cached call
        (this.$vnode && this.$vnode.ssrContext) || // stateful
        (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
      // 2.2 with runInNewContext: true
      if(! context &&typeof__VUE_SSR_CONTEXT__ ! = ='undefined') {
        context = __VUE_SSR_CONTEXT__
      }
      // inject component styles
      if (injectStyles) {
        injectStyles.call(this, context)
      }
      // register component module identifier for async chunk inferrence
      if (context && context._registeredComponents) {
        context._registeredComponents.add(moduleIdentifier)
      }
    }
    // used by ssr in case component is cached and beforeCreate
    // never gets called
    options._ssrRegister = hook
  } else if (injectStyles) {
    hook = injectStyles
  }

  if (hook) {
    var functional = options.functional
    var existing = functional
      ? options.render
      : options.beforeCreate

    if(! functional) {// inject component registration as beforeCreate hook
      options.beforeCreate = existing
        ? [].concat(existing, hook)
        : [hook]
    } else {
      // for template-only hot-reload because in that case the render fn doesn't
      // go through the normalizer
      options._injectStyles = hook
      // register for functioal component in vue file
      options.render = function renderWithStyleInjection (h, context) {
        hook.call(context)
        return existing(h, context)
      }
    }
  }

  return {
    esModule: esModule,
    exports: scriptExports,
    options: options
  }
}

Copy the code

Look at the exports of app.vue after normalizeComponent

This is what you end up importing when you import the vue file, compared to the output from the script section you saw earlier:

  1. Add the render and staticRenderFns attributes, which are taken from the compiled results in the Template section;
  2. We also add a beforeCreate hook property whose value is an array containing injectStyle. When the component is instantiated, the beforeCreate hook is triggered. InjectStyle is executed and the component style takes effect.

Four,

When we import a vue file, it triggers the Loader mechanism of WebPack, and uses the Vue-Loader to process the vue file. The VUe-Loader processes the vUE file into three parts, template, Script and style, according to the tag: In the template part, the ue-template-compiler is introduced to compile the template and output the rendering function. The script part is compiled by Babel-Loader to generate code that can be run directly by the browser. The style section is not handled directly, but a handler function is defined. The handler is handled first by csS-Loader and then by Vue style-Loader. Finally, normalizeComponent method is used to synthesize the three parts and output the final result.