Vue Loader

介紹

  • 容許爲 Vue 組件的每一個部分使用其它的 webpack loader,例如在 <style> 的部分使用 Sass 和在 <template> 的部分使用 Pug(模板引擎)
  • 容許在一個 .vue 文件中使用自定義塊,並對其運用自定義的 loader 鏈;

起步

  • Vue Loader 的配置和其它的 loader 不太同樣。除了經過一條規則將 vue-loader 應用到全部擴展名爲 .vue 的文件上以外,請確保在你的 webpack 配置中添加 Vue Loader 的插件
// webpack.config.js
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的熱重載
          'css-loader'    // 用來處理css中的資源路徑爲模塊,同時支持CSS Modules(<style module>)
        ]
      }
    ]
  },
  plugins: [
    // 請確保引入這個插件來施展魔法。它的職責是將你定義過的其它規則複製並應用到 .vue 文件裏相應語言的塊。例如,若是你有一條匹配 /\.js$/ 的規則,那麼它會應用到 .vue 文件裏的 <script> 塊。
    new VueLoaderPlugin()
  ]
}
  • 選項參考?是vue loader的選項嗎?

處理資源路徑

  • 當 Vue Loader 編譯單文件組件中的 <template> 塊時,它也會將全部遇到的資源 URL 轉換爲 webpack 模塊請求。
<img src="../image.png">
// 編譯成
createElement('img', {
  attrs: {
    src: require('../image.png') // 如今這是一個模塊的請求了
  }
})
  • 默認下列標籤/特性的組合會被轉換,且這些組合時可使用 transformAssetUrls 選項進行配置的。
{
  video: ['src', 'poster'],
  source: 'src',
  img: 'src',
  image: 'xlink:href'
}
  • 若是你配置了爲 <style> 塊使用 css-loader,則你的 CSS 中的資源 URL 也會被同等處理。

轉換規則

  • 若是路徑是絕對路徑 (例如 /images/foo.png),會原樣保留。
  • 若是路徑以 . 開頭,將會被看做相對的模塊依賴,並按照你的本地文件系統上的目錄結構進行解析。
  • 若是路徑以 ~ 開頭,其後的部分將會被看做模塊依賴。這意味着你能夠用該特性來引用一個 Node 依賴中的資源
  • 若是路徑以 @ 開頭,也會被看做模塊依賴。若是你的 webpack 配置中給 @ 配置了 alias,這就頗有用了。全部 vue-cli 建立的項目都默認配置了將 @ 指向 /src。

相關的 Loader

  • 由於像 .png 這樣的文件不是一個 JavaScript 模塊,你須要配置 webpack 使用 file-loader 或者 url-loader 去合理地處理它們。經過 Vue CLI 建立的項目已經把這些預配置好了。
    • file-loader 能夠指定要複製和放置資源文件的位置,以及如何使用版本哈希命名以得到更好的緩存。此外,這意味着 你能夠就近管理圖片文件,可使用相對路徑而不用擔憂部署時 URL 的問題。使用正確的配置,webpack 將會在打包輸出中自動重寫文件路徑爲正確的 URL。
    • url-loader 容許你有條件地將文件轉換爲內聯的 base-64 URL (當文件小於給定的閾值),這會減小小文件的 HTTP 請求數。若是文件大於該閾值,會自動的交給 file-loader 處理。

使用預處理器

  • style中會根據 lang 特性以及你 webpack 配置中的規則自動推斷出要使用的 loader

Sass

npm install -D sass-loader node-sass


module.exports = {
  module: {
    rules: [
      // ... 忽略其它規則

      // 普通的 `.scss` 文件和 `*.vue` 文件中的
      // `<style lang="scss">` 塊都應用它
      {
        test: /\.scss$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  },
  // 插件忽略
}
  • 可以 import 'style.scss' ?

Sass vs SCSS

  • SCSS是Sass 3引入的新語法,爲了向css3靠攏
  • sass-loader 會默認處理不基於縮進的 scss 語法。爲了使用基於縮進的 sass 語法,你須要向這個 loader 傳遞選項:
// webpack.config.js -> module.rules
{
  test: /\.sass$/,
  use: [
    'vue-style-loader',
    'css-loader',
    {
      loader: 'sass-loader',
      options: {
        indentedSyntax: true
      }
    }
  ]
}

共享全局變量

  • sass-loader 也支持一個 data 選項,這個選項容許你在全部被處理的文件之間共享常見的變量,而不須要顯式地導入它們:
// webpack.config.js -> module.rules
{
  test: /\.scss$/,
  use: [
    'vue-style-loader',
    'css-loader',
    {
      loader: 'sass-loader',
      options: {
        // 你也能夠從一個文件讀取,例如 `variables.scss` ?
        data: `$color: red;`
      }
    }
  ]
}

Less

npm install -D less less-loader

// webpack.config.js -> module.rules
{
  test: /\.less$/,
  use: [
    'vue-style-loader',
    'css-loader',
    'less-loader'
  ]
}

Stylus

npm install -D stylus stylus-loader

// webpack.config.js -> module.rules
{
  test: /\.styl(us)?$/,
  use: [
    'vue-style-loader',
    'css-loader',
    'stylus-loader'
  ]
}

PostCSS

  • PostCSS 的配置能夠經過 postcss.config.js 或 postcss-loader 選項來完成。postcss-loader 也能夠和上述其它預處理器結合使用。
  • Vue Loader v15 再也不默認應用 PostCSS 變換。你須要經過 postcss-loader 使用 PostCSS。
npm install -D postcss-loader

// webpack.config.js -> module.rules
{
  test: /\.css$/,
  use: [
    'vue-style-loader',
    {
      loader: 'css-loader',
      options: { importLoaders: 1 }
    },
    'postcss-loader'
  ]
}

Babel

  • Babel 的配置能夠經過 .babelrc 或 babel-loader 選項來完成。
npm install -D babel-core babel-loader

// webpack.config.js -> module.rules
{
  test: /\.js?$/,
  loader: 'babel-loader'
}

排除 node_modules

  • 若是你導入一個 node_modules 內的 Vue 單文件組件,它的 <script> 部分在轉譯時將會被排除在外。
  • 爲了確保 JS 的轉譯應用到 node_modules 的 Vue 單文件組件,你須要經過使用一個排除函數將它們加入白名單:
{
  test: /\.js$/,
  loader: 'babel-loader',
  exclude: file => (
    /node_modules/.test(file) &&
    !/\.vue\.js/.test(file)    // 正則匹配.vue.js後綴的文件,是由於.vue被vue loader處理後js會生成後綴未.vue.js的js吧
  )
}

TypeScript

  • TypeScript 的配置能夠經過 tsconfig.json 來完成。
npm install -D typescript ts-loader

// webpack.config.js
module.exports = {
  resolve: {
    // 將 `.ts` 添加爲一個可解析的擴展名。
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      // ... 忽略其它規則
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        options: { appendTsSuffixTo: [/\.vue$/] }
      }
    ]
  },
  // ...plugin omitted
}

pug

  • 模板的處理會稍微有些不一樣,由於絕大對數 webpack 的模板類 loader,諸如 pug-loader,會返回一個模板函數而不是一個編譯好的 HTML 字符串
  • 一個返回原始的 HTML 字符串的 loader,例如 pug-plain-loader
// .vue中
<template lang="pug">
div
  h1 Hello world!
</template>

// webpack中
npm install -D pug pug-plain-loader

// webpack.config.js -> module.rules
{
  test: /\.pug$/,
  loader: 'pug-plain-loader'
}
  • 你還打算使用它在 JavaScript 中將 .pug 文件做爲字符串導入,你須要在這個預處理 loader 以後鏈上 raw-loader
// webpack.config.js -> module.rules
{
  test: /\.pug$/,
  oneOf: [    // 規則數組,當規則匹配時,只使用第一個匹配規則。
    // 這條規則應用到 Vue 組件內的 `<template lang="pug">`
    {
      resourceQuery: /^\?vue/,
      use: ['pug-plain-loader']
    },
    // 這條規則應用到 JavaScript 內的 pug 導入
    {
      use: ['raw-loader', 'pug-plain-loader']
    }
  ]
}

Scoped CSS

  • 當 <style> 標籤有 scoped 屬性時,它的 CSS 只做用於當前組件中的元素。它經過使用 PostCSS 來實現如下轉換(不是說再也不默認使用嗎?)
<style scoped>
.example {
  color: red;
}
</style>

<template>
  <div class="example">hi</div>
</template>

// 轉換爲
<style>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>

混用本地和全局樣式

  • 能夠在一個組件中同時使用有 scoped 和非 scoped 樣式

子組件的根元素

  • 使用 scoped 後,父組件的樣式將不會滲透到子組件中。不過一個子組件的根節點會同時受其父組件的 scoped CSS 和子組件的 scoped CSS 的影響。這樣設計是爲了讓父組件能夠從佈局的角度出發,調整其子組件根元素的樣式。

深度做用選擇器

  • 若是你但願 scoped 樣式中的一個選擇器可以做用得「更深」,例如影響子組件,你可使用 >>> 操做符。有些像 Sass 之類的預處理器沒法正確解析 >>>。這種狀況下你可使用 /deep/ 操做符取而代之
<style scoped>
.a >>> .b { /* ... */ }
</style>
// 編譯爲
.a[data-v-f3f3eg9] .b { /* ... */ }

動態生成的內容

  • 經過 v-html 建立的 DOM 內容不受 scoped 樣式影響,可是你仍然能夠經過深度做用選擇器來爲他們設置樣式。

注意事項

  • 當 p { color: red } 是 scoped 時 (即與特性選擇器組合使用時) 會慢不少倍。若是你使用 class 或者 id 取而代之,好比 .example { color: red },性能影響就會消除。

CSS Modules

  • vue-loader 提供了與 CSS Modules 的一流集成,能夠做爲模擬 scoped CSS 的替代方案。
  • CSS Modules經過對css中選擇器的編譯,來實現該種選擇器在全局中的惟一性

用法

  • 經過向 css-loader 傳入 modules: true 來開啓(vue-cli默認開啓)
// webpack.config.js
{
  module: {
    rules: [
      // ... 其它規則省略
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          {
            loader: 'css-loader',
            options: {
              // 開啓 CSS Modules
              modules: true,
              // 自定義生成的類名
              localIdentName: '[local]_[hash:base64:8]'
            }
          }
        ]
      }
    ]
  }
}
  • module 特性指引 Vue Loader 做爲名爲 $style 的計算屬性,向組件注入 CSS Modules 局部對象。而後你就能夠在模板中經過一個動態類綁定來使用它了:
<style module>    // 在你的 <style> 上添加 module 特性
.red {
  color: red;
}
.bold {
  font-weight: bold;
}
</style>

<template>
  <p :class="$style.red">    // "red_1VyoJ-uZ"由開啓時自定義生成的類名決定
    This should be red
  </p>
</template>

可選用法

  • 若是你只想在某些 Vue 組件中使用 CSS Modules,你可使用 oneOf 規則並在 resourceQuery 字符串中檢查 module 字符串(vue-cli好像默認開啓,只有style擁有module屬性纔會是模塊化樣式)
// webpack.config.js -> module.rules
{
  test: /\.css$/,
  oneOf: [
    // 這裏匹配 `<style module>`
    {
      resourceQuery: /module/,
      use: [
        'vue-style-loader',
        {
          loader: 'css-loader',
          options: {
            modules: true,
            localIdentName: '[local]_[hash:base64:5]'
          }
        }
      ]
    },
    // 這裏匹配普通的 `<style>` 或 `<style scoped>`
    {
      use: [
        'vue-style-loader',
        'css-loader'
      ]
    }
  ]
}

和預處理器配合使用

  • CSS Modules 能夠與其它預處理器一塊兒使用
// webpack.config.js -> module.rules
{
  test: /\.scss$/,
  use: [
    'vue-style-loader',
    {
      loader: 'css-loader',
      options: { modules: true }
    },
    'sass-loader'
  ]
}

自定義的注入名稱

  • 這時this下會綁定一個名爲a得計算屬性
<div :class='a.about'></div>


<style module="a">
  /* 注入標識符 a */
</style>

熱重載

  • 當你修改 .vue 文件時,該組件的全部實例將在不刷新頁面的狀況下被替換。它甚至保持了應用程序和被替換組件的當前狀態!當你調整模版或者修改樣式時,這極大地提升了開發體驗。

狀態保留規則

  • 當編輯一個組件的 <template> 時,這個組件實例將就地從新渲染,並保留當前全部的私有狀態。可以作到這一點是由於模板被編譯成了新的無反作用的渲染函數。(template實際上被渲染爲render函數,應該是沒法比較函數之間的異同的,經過比較兩個函數返回的v-node樹的異同,來判斷哪些節點須要銷燬,熱更新的效果應該和渲染的原理同樣;只是生成新的渲染,它自己對傳入的參數值沒有任何改動,因此不會影響到其餘元素的渲染效果,能夠作到無反作用。生命週期鉤子可能會在執行過程當中改變這些變量,因此必須整個組件銷燬重建,來觸發這些生命週期函數;當傳入值prop改變時,組件內部沒有變更,不會引發反作用,依然採用構建v-node樹進行比較最小替換的方式)
  • 當編輯一個組件的 <script> 時,這個組件實例將就地銷燬並從新建立。(應用中其它組件的狀態將會被保留) 是由於 <script> 可能包含帶有反作用的生命週期鉤子,因此將從新渲染替換爲從新加載是必須的,這樣作能夠確保組件行爲的一致性。這也意味着,若是你的組件帶有全局反作用,則整個頁面將會被從新加載。
  • <style> 會經過 vue-style-loader 自行熱重載,因此它不會影響應用的狀態。style的改變彷佛不會改變this.$style指向的對象,否則應該觸發組件銷燬重建的

用法

  • 當手動設置你的工程時,熱重載會在你啓動 webpack-dev-server --hot 服務時自動開啓。

關閉熱重載

  • 熱重載默認是開啓的,除非遇到如下狀況
    • webpack 的 target 的值是 node (服務端渲染)
    • webpack 會壓縮代碼(生產環境?)
    • process.env.NODE_ENV === 'production'
  • 能夠設置 hotReload: false 選項來顯式地關閉熱重載:
module: {
  rules: [
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        hotReload: false // 關閉熱重載
      }
    }
  ]
}

自定義塊

  • 在 .vue 文件中,你能夠自定義語言塊。應用於一個自定義塊的 loader 是基於這個塊的 lang 特性、塊的標籤名以及你的 webpack 配置進行匹配的。若是找到了一個自定義塊的匹配規則,它將會被處理,不然該自定義塊會被默默忽略。若是這個自定義塊被全部匹配的 loader 處理以後導出一個函數做爲最終結果,則這個 *.vue 文件的組件會做爲一個參數被這個函數調用。
  • 使用 resourceQuery 來爲一個沒有 lang 的自定義塊匹配一條規則。例如爲了匹配自定義塊 <docs>:
//自定義loader用以處理docs標籤
module.exports = function (source, map) {
    this.callback(
        null,
        `export default function (Component) {    // loader導出的函數,Component指向.vue文件(.vue文件的options好像被實例this繼承,沒有在其餘文檔看到,這個對象包含什麼?)
        Component.options.__docs = ${
        JSON.stringify(source)
        }
      }`,
        map
    )
}

// webpack配置
{
  module: {
    rules: [
      {
        resourceQuery: /blockType=docs/,
        loader: require.resolve('./docs-loader.js')
      }
    ]
  }
}

// 子組件使用(@/components/HelloWorld.vue)
<docs>
This is the documentation for component B.
</docs>

// 父組件引用
import HelloWorld from '@/components/HelloWorld.vue'

HelloWorld.__docs    =>    JSON.stringify('This is the documentation for component B.')

CSS 提取

  • 請只在生產環境下使用 CSS 提取,這將便於你在開發環境下進行熱重載。(vue-cli中,開發環境css經過js動態寫入head中,生產環境則會依據build和check分別打包)

webpack 4

npm install -D mini-css-extract-plugin

// webpack.config.js
var MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // 其它選項...
  module: {
    rules: [
      // ... 忽略其它規則
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV !== 'production'
            ? 'vue-style-loader'
            : MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // ... 忽略 vue-loader 插件
    new MiniCssExtractPlugin({
      filename: style.css
    })
  ]
}

webpack 3

  • 這裏應該須要對不一樣開發環境配置不一樣的webpack配置
npm install -D extract-text-webpack-plugin

// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
  // 其它選項...
  module: {
    rules: [
      // ...其它規則忽略
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract({
          use: 'css-loader',
          fallback: 'vue-style-loader'
        })
      }
    ]
  },
  plugins: [
    // ...vue-loader 插件忽略
    new ExtractTextPlugin("style.css")
  ]
}

代碼校驗

ESLint

  • 官方的 eslint-plugin-vue 同時支持在 Vue 單文件組件的模板和腳本部分的代碼校驗。只須要在你的 ESLint 配置文件中使用該插件要導入的配置:
// .eslintrc.js
module.exports = {
  extends: [
    "plugin:vue/essential"
  ]
}
  • 能夠經過命令行用eslint校驗某個.vue文件
eslint --ext js,vue MyComponent.vue
  • 使用 eslint-loader開發過程當中每次保存的時候就會自動進行代碼校驗
npm install -D eslint eslint-loader

// webpack.config.js
module.exports = {
  // ... 其它選項
  module: {
    rules: [
      {
        enforce: 'pre',    // 請確保它是做爲一個 pre-loader 運用的,loader的種類,影響執行的順序,執行書序分爲:後置(post)、行內(normal)、普通(inline)、前置(pre)
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      }
    ]
  }
}

stylelint

  • 支持在 Vue 單文件組件的樣式部分的代碼校驗。(vue-cli好像默認沒裝)
  • 命令行校驗
stylelint MyComponent.vue
  • 使用插件實現自動校驗
npm install -D stylelint-webpack-plugin

/ webpack.config.js
const StyleLintPlugin = require('stylelint-webpack-plugin');
module.exports = {
  // ... 其它選項
  plugins: [
    new StyleLintPlugin({
      files: ['**/*.{vue,htm,html,css,sss,less,scss,sass}'],
    })
  ]
}
相關文章
相關標籤/搜索