【Vue】.vue 單文件組件與 Webpack vue-loader 配置

Vue Loader 是一個Webpack的 loader,它支持 .vue 單文件組件的各個<template><script><style>以及自定義語言塊使用非默認語言,好比 style 使用 less/sass 語言,並支持組件中 css scoped 局部做用域和 css module 等。javascript

若是在項目開始階段爲前端工程化複雜的配置而困惑,不妨試試使用 IDE 自動構建,推薦使用 WebStorm 新建 Vue 項目,參考 WebStorm 2018 的破解、漢化與設置使用

1、.vue 單文件組件 (SFC) 規範

.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

  • 絕對路徑原樣保存;
  • 以「.」開頭,則看作相對模塊請求,並根據文件系統上的目錄結構解析;
  • 以「~」開頭,則將其後的做爲模塊請求,能夠引入 node_modules 中的資源;
  • 以「@」開頭,則其後的內容也被解釋爲模塊請求,「@」在 vue-cli 建立的項目中默認指向"/src"

2、Webpack 配置 vue-loader

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()
  ]
}

3、Lang 預處理

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 使用預處理器

4、CSS Style 的特殊用法

4.1 CSS Scoped 局部做用域

<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 時須要注意如下幾點:

  • 使用 scope 做用域不能棄用 class 或 id 等!

    考慮到瀏覽器渲染各類 CSS 選擇器的方式,當使用 scoped 時,選擇屬性選擇器如 p { color: red } 在做用域中會慢不少倍(即當與屬性選擇器組合時)。若是你使用 class 或者 id 代替,好比 .example { color: red },這樣幾乎沒有性能影響

  • 子組件的根節點將受父組件 css scoped 做用域影響

    使用 scope 做用域時,父組件的樣式不會泄漏到子組件中。 但子組件的根節點將受父級做用域 CSS 和子級做用域 CSS 的影響。 這是爲了父級能夠設置子組件根元素的樣式以進行佈局。

  • 父組件可使用‘ >>> ’或‘ /deep/ ’ 這種深度選擇器做用於子組件

    <style scoped>
    .a >>> .b { /* ... */ }
    </style>

    編譯爲

    .a[data-v-f3f3eg9] .b { /* ... */ }
  • 經過 v-html 動態建立的 DOM 內容不受 scoped 樣式影響,但可使用深度選擇器進行樣式改變
在 template 中只包含一個外層節點,不能多個節點並列,這個設計思路遵循父組件能夠操做子節點的一個根節點,即便在 CSS 局部做用域下依然有效

4.2 CSS Modules 模塊模式

一個 CSS Module 其實就是一個 CSS 類型的文件,用於模塊化和組合 CSS 的系統,在<style>上添加 module 特性並配置 vue-loader 的 modules 模式,就可使用 CSS Modules。

4.2.1 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 }
    }
  ]
}

4.2.2 CSS Modules 使用

在 .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中。

4.3 CSS 提取

一些插件能夠將 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

5、自定義語言塊

除了默認的<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 官網

相關文章
相關標籤/搜索