筆者系 vue-loader 貢獻者(#16)之一css
vue-loader is a loader for Webpack that can transform Vue components written in the following format into a plain JavaScript modulehtml
簡單來講就是:將 .vue 文件變成 .bundle.js,而後放入瀏覽器運行。vue
測試是最好的文檔,因此咱們從測試用例開始分析,找到test/fixture/basic.vue,內容以下:java
<template> <h2 class="red">{{msg}}</h2> </template> <script> export default { data () { return { msg: 'Hello from Component A!' } } } </script> <style> comp-a h2 { color: #f00; } </style>
經過運行測試以後,能夠獲得如下輸出,可是因爲文件巨大,筆者只抽出部分開始分析,以下node
/* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./lib/selector.js?type=script&index=0&bustCache!./test/fixtures/basic.vue /* harmony default export */ var basic = ({ data() { return { msg: 'Hello from Component A!' }; } }); // CONCATENATED MODULE: ./lib/template-compiler?{"id":"data-v-b647d0ce","hasScoped":false,"buble":{"transforms":{}}}!./lib/selector.js?type=template&index=0&bustCache!./test/fixtures/basic.vue var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c("h2", { staticClass: "red" }, [_vm._v(_vm._s(_vm.msg))]) } var staticRenderFns = [] render._withStripped = true var esExports = { render: render, staticRenderFns: staticRenderFns } /* harmony default export */ var fixtures_basic = (esExports); if (false) { module.hot.accept() if (module.hot.data) { require("vue-hot-reload-api") .rerender("data-v-b647d0ce", esExports) } } // CONCATENATED MODULE: .!./test/fixtures/basic.vue var disposed = false function injectStyle (ssrContext) { if (disposed) return __webpack_require__(3) } var normalizeComponent = __webpack_require__(8) /* script */ /* template */ /* 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( basic, fixtures_basic, __vue_template_functional__, __vue_styles__, __vue_scopeId__, __vue_module_identifier__ ) Component.options.__file = "test/fixtures/basic.vue" if (Component.esModule && Object.keys(Component.esModule).some(function (key) { return key !== "default" && key.substr(0, 2) !== "__"})) { console.error("named exports are not supported in *.vue files.")} })()}
以上的輸出就是最終能夠拿到瀏覽器上運行的 javaScript,儘管筆者已經刪除了一些會影響理解的部分代碼,可是這麼直接觀察這個文件,不免仍是無從下手。webpack
那麼咱們繼續細化分析步驟,vue-loader 將 basic.vue 編譯到最終輸出的 bundle.js 的過程當中,其實調用了四個小的 loader。它們分別是:git
以上四個 loader ,除了 babel-loader 是外部的package,其餘三個都存在於 vue-loader 的內部(lib/style-compiler 和 lib/template-compiler 和 lib/selector)。github
首先 vue-loader 將 basic.vue 編譯成如下內容web
/* script */ import __vue_script__ from "!!babel-loader!../../lib/selector?type=script&index=0&bustCache!./basic.vue" /* template */ import __vue_template__ from "!!../../lib/template-compiler/index?{\"id\":\"data-v-793be54c\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!../../lib/selector?type=template&index=0&bustCache!./basic.vue" /* styles */ import __vue_styles__ from "!!vue-style-loader!css-loader!../../lib/style-compiler/index?{\"vue\":true,\"id\":\"data-v-793be54c\",\"scoped\":false,\"hasInlineConfig\":false}!../../lib/selector?type=styles&index=0&bustCache!./basic.vue" var Component = normalizeComponent( __vue_script__, __vue_template__, __vue_template_functional__, __vue_styles__, __vue_scopeId__, __vue_module_identifier__ )
爲了方便理解,筆者刪除修改了一些內容。後端
在三個 import 語句中,無論它們用了多少個不一樣的 loader 去加載,loader chain 的源頭都是 basic.vue。
首先分析 script 部分
/* script */ import __vue_script__ from "!!babel-loader!../../lib/selector?type=script&index=0&bustCache!./basic.vue"
從作右到左,也就是 basic.vue 被前後被 selector 和 babel-loader 處理過了。
selector(參數type=script) 的處理結果是將 basic.vue 中的 javaScript 抽出來以後交給babel-loader去處理,最後生成可用的 javaScript
再來分析 template 部分
/* template */ import __vue_template__ from "!!../../lib/template-compiler/index?{\"id\":\"data-v-793be54c\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!../../lib/selector?type=template&index=0&bustCache!./basic.vue"
一樣的,從左到右,basic.vue 前後被 selector 和 template-compiler 處理過了。
selector (參數type=template) 的處理結果是將 basic.vue 中的 template 抽出來以後交給 template-compiler 處理,最終輸出成可用的 HTML。
最後分析 style 部分
/* styles */ import __vue_styles__ from "!!vue-style-loader!css-loader!../../lib/style-compiler/index?{\"vue\":true,\"id\":\"data-v-793be54c\",\"scoped\":false,\"hasInlineConfig\":false}!../../lib/selector?type=styles&index=0&bustCache!./basic.vue"
style 涉及的 loader 較多,一個一個來分析, 從上代碼可知,basic.vue 前後要被 selector, style-compiler, css-loader 以及 vue-style-loader 處理。
selector (參數type=style) 的處理結果是將 basic.vue 中的 css 抽出來以後交給 style-compiler 處理成 css, 而後交給 css-loader 處理生成 module, 最後經過 vue-style-loader 將 css 放在 <style>
裏面,而後注入到 HTML 裏。
注意,這裏之因此沒有用 style-loader 是由於 vue-style-loader 是在 fork 了 style-loader 的基礎上,增長了後端繪製 (SSR) 的支持。具體的不一樣,讀者能夠查看官方文檔,筆者這裏再也不累述。
經過上面的介紹,想必讀者已經對 vue-loader 以及它涉及的幾個 loader 的做用有了一個大概的瞭解。
那麼接下來,在後續文章中咱們來開始一個個分析這幾個 loader 的源碼。