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:
- Add the render and staticRenderFns attributes, which are taken from the compiled results in the Template section;
- 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.