Vue Loader 是一個Webpack
的 loader,它支持 .vue 單文件組件的各個<template>
、<script>
和 <style>
以及自定義語言塊使用非默認語言,好比 style 使用 less/sass 語言,並支持組件中 css scoped 局部做用域和 css module 等。javascript
若是在項目開始階段爲前端工程化複雜的配置而困惑,不妨試試使用 IDE 自動構建,推薦使用 WebStorm 新建 Vue 項目,參考 WebStorm 2018 的破解、漢化與設置使用
.vue 文件是一個自定義的文件類型,用類 HTML 語法描述一個 Vue 組件。每一個 .vue 文件包含三種類型的頂級語言塊 <template>
、<script>
和 <style>
,還容許添加可選的自定義塊:css
<template>
模板塊一個SFC中最多一個< template >塊;其內容將被提取爲字符串傳遞給 vue-template-compiler ,而後 webpack 將其編譯爲 js 渲染函數,並最終注入到從<script>
導出的組件中;html
<script>
腳本塊一個SFC最多一個<script>
塊,會做爲一個模塊封裝。它的默認導出應該是一個 Vue.js 的組件選項對象,也能夠導出由 Vue.extend()
建立的擴展對象。前端
<style>
樣式塊一個 .vue 文件能夠包含多個 <style>
標籤,標籤能夠有 scoped
或者 module
屬性。vue
vue-loader 將會使用塊名來查找對應的 loader 進行處理。java
一個 .Vue 文件示例以下:node
<template src="./template.html"> <div class="example">{{ msg }}</div> </template> <script src="./script.js"> export default { data () { return { msg: 'Hello world!' } } } </script> <style scoped src="./style.css"> .example { color: red; } </style> <custom1> This could be e.g. documentation for the component. </custom1>
全部語言塊支持 src 導入,導入路徑遵循和 webpack 模塊請求相同的路徑解析規則。當 Vue Loader 編譯單文件組件中的 <template>
塊時也會將全部遇到的資源 URL 轉換爲 webpack 模塊請求。webpack
路徑解析規則:git
- 絕對路徑原樣保存;
Vue Loader 和 Webpack 結合,使咱們可使用 .vue 文件來開發 Vue 項目。Vue Loader 的配置和其它的 loader 不太同樣。除了經過 rules 規則將 vue-loader 應用到 .vue 的文件上以外,還須要添加 Vue Loader Plugin 插件:github
// webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { module: { rules: [ // ... 其它規則 { test: /\.vue$/, loader: 'vue-loader' } ] }, plugins: [ new VueLoaderPlugin() ] }
Vue Loader Plugin 的職責是將 .vue 文件中的語言塊應用在相應的 rules 上。例如樣式匹配 /\.css$/
的規則,那麼它會應用到 .vue 文件裏的 <style>
塊,匹配 /\.js$/
的規則會應用到 .vue 文件裏的 <script>
塊。
因此修改上訴配置示例,使其更加完整:
// webpack.config.js const path = require('path') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { mode: 'development', module: { rules: [ // ... 其它規則 { test: /\.vue$/, loader: 'vue-loader' }, // 它會應用到普通的 `.js` 文件,以及 `.vue` 文件中的 `<script>` 塊 { test: /\.js$/, loader: 'babel-loader' }, // 它會應用到普通的 `.css` 文件,以及 `.vue` 文件中的 `<style>` 塊 { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] } ] }, plugins: [ // 這個插件使 .vue 中的各種語言塊匹配相應的規則 new VueLoaderPlugin() ] }
Vue 支持各種型的預處理器來編譯語言塊,Vue 會根據語言塊的lang
屬性和 webpack 配置的 option rules 自動推斷相應的 loader。
例如讓 css 使用 sass 語言,須要先安裝 sass loader 加載器,並在 webpack 配置纔可使用:
$ npm install sass-loader node-sass --save-dev
// webpack.config.js -> module.rules { test: /\.sass$/, use: [ 'vue-style-loader', 'css-loader', { loader: 'sass-loader', options: { indentedSyntax: true //sass-loader 默認解析 SCSS 語言 } } ] }
在 .vue 文件中的<style>
增長 lang 屬性並賦值,就可使用 sass 語法編寫 css:
<style lang="sass"> /* write SASS here */ $base-color: #F90; article{ h1 {color: #333} p {color: $base-color} } </style>
編譯後的 style 樣式表爲:
article h1 {color: #333} article p {color: #F90}
除了 Vue 默認使用 PostCSS 處理 css,還可使用 sass,less,stylus 外, 對於 js 部分 Vue 默認使用 babel 處理,還能夠用 coffee、typescript 等,具體的 loader & webpack 配置方式見 Vue Loader 使用預處理器
當<style>
標籤帶 scoped 屬性時,創造出 css 的「局部做用域」 只做用於當前組件的元素,相似 Shadow Dom 中的樣式封裝。能夠在同一個組件中使用 scoped 跟 non-scoped styles,以下所示:
<template> <div class="example">hi</div> </template> <style> /* global styles */ </style> <style scoped> /* local styles */ .example { color: red; } </style>
經過使用 PostCSS 進行做用域重寫,處理後以下:
<style> .example[data-v-f3f3eg9] { color: red; } </style> <template> <div class="example" data-v-f3f3eg9>hi</div> </template>
當使用 css scoped 時須要注意如下幾點:
考慮到瀏覽器渲染各類 CSS 選擇器的方式,當使用 scoped 時,選擇屬性選擇器如 p { color: red } 在做用域中會慢不少倍(即當與屬性選擇器組合時)。若是你使用 class 或者 id 代替,好比 .example { color: red },這樣幾乎沒有性能影響
使用 scope 做用域時,父組件的樣式不會泄漏到子組件中。 但子組件的根節點將受父級做用域 CSS 和子級做用域 CSS 的影響。 這是爲了父級能夠設置子組件根元素的樣式以進行佈局。
父組件可使用‘ >>> ’或‘ /deep/ ’ 這種深度選擇器做用於子組件
<style scoped> .a >>> .b { /* ... */ } </style>
編譯爲
.a[data-v-f3f3eg9] .b { /* ... */ }
在 template 中只包含一個外層節點,不能多個節點並列,這個設計思路遵循父組件能夠操做子節點的一個根節點,即便在 CSS 局部做用域下依然有效
一個 CSS Module 其實就是一個 CSS 類型的文件,用於模塊化和組合 CSS 的系統,在<style>
上添加 module 特性並配置 vue-loader 的 modules 模式,就可使用 CSS Modules。
CSS Modules 必須經過向 css-loader 傳入 modules: true 來開啓
// webpack.config.js -> module.rules { test: /\.css$/, use: [ 'vue-style-loader', { loader: 'css-loader', options: { modules: true, // 開啓 CSS Modules localIdentName: '[local]_[hash:base64:8]' // 自定義生成的類名 } } ] }
CSS Modules 能夠與其它預處理器一塊兒使用,例如lang="sass"
時的配置以下:
// webpack.config.js -> module.rules { test: /\.scss$/, use: [ 'vue-style-loader', { loader: 'css-loader', options: { modules: true } }, { loader: 'sass-loader', options: { indentedSyntax: true } } ] }
在 .vue 中的 <style>
上添加 module 特性,Vue Loader 會將這個 CSS Modules 編譯爲$style
計算屬性注入到組件中。能夠在<template>
中經過動態類綁定來使用這個 CSS Modules 局部對象:
<style module> .red { color: red; } </style> <template> <p :class="$style.red"> This should be red </p> <p :class="{ [$style.red]: isRed }"> Am I red? </p> </template>
JS 也能夠經過 this.$style
訪問 CSS Modules:
<script> export default { created () { console.log(this.$style.red) // "red_1VyoJ-uZ" } } </script>
上述代碼中的this.$style.red
是一個基於文件名和類名生成的標識符。當從 JS 模塊導入 CSS 模塊時,它會導出包含從本地名稱到全局名稱的全部映射的一個對象
在 CSS Module 中,全部的 url 和 @import 都是被當作模塊依賴,例如 url(./image.png) 會被轉換爲 require(‘./image.png’),請求的資源能夠是在
node_modules
中。
一些插件能夠將 CSS 提取到單獨的文件中,爲每一個包含 CSS 的 JS 文件建立一個 CSS 文件,以便按需加載 CSS 和源代碼。
webpack4 使用 mini-css-extract-plugin 插件,配置以下:
npm install -D mini-css-extract-plugin
只在生產環境下使用 CSS 提取,便於在開發環境下進行熱重載。
// webpack.config.js var MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: process.env.NODE_ENV === 'development', }, }, 'css-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // 相似於webpackOptions.output中的相同選項 chunkFilename: '[id].css' }) ] }
更多配置參考 github mini-css-extract-plugin
除了默認的<template>
等節點,還能夠加自定義節點,並在webpack 配置的 loader 處理自定義語言塊;
能夠給節點加 lang 屬性,此時節點 rule 匹配 lang 的擴展;若是沒有標記 lang,則自定義節點的 name 和 rule 須要在 webpack 中聲明。
舉例自定義一個語言塊 <myblock>
,首先須要自定義 loader 將自定義塊注入到組件中,自定義的 myblock-loader 以下:
// myblock-loader.js module.exports = function (source, map) { this.callback( null, `export default function (Component) { Component.options.__myblock = ${ JSON.stringify(source) } }`, map ) }
配置到 webpack
// webpack.config.js -> module.rules rules: [ { resourceQuery: /blockType=docs/, loader: require.resolve('./docs-loader.js') } ]
在組件中使用:
<!-- ComponentB.vue --> <template> <div>Hello</div> </template> <myblock> This is the documentation for component B. </myblock>
組件間複用:
<!-- ComponentA.vue --> <template> <div> <ComponentB/> <p>{{ myblock }}</p> </div> </template> <script> import ComponentB from './ComponentB.vue'; export default { components: { ComponentB }, data () { return { myblock: ComponentB.__myblock } } } </script>
更多內容參考 Vue Loader 官網