前端工程師都得掌握的 webpack Loader

原文地址javascript

0. 前言

本文將 webpackLoader 相關的知識點整理了一下,部分文字是從官方文檔中直接摘錄過來的,並附上本身的理解。若是以爲看起來和官方文檔差很少,直接看官方文檔最好啦~css


webpack loaders 從上手到理解系列 還有這些:vue

1. 簡述 webpack 工做流程

本文不過多描述 webpack 的做用和使用方法,若是還不是太熟悉,能夠打開 webpack.js.org/ 先熟悉一下。java

關於 webpack 的工做流程,簡單來講能夠歸納爲如下幾步:node

  1. 參數解析
  2. 找到入口文件
  3. 調用 Loader 編譯文件
  4. 遍歷 AST,收集依賴
  5. 生成 Chunk
  6. 輸出文件

其中,真正起編譯做用的即是 Loader,本文也就 Loader 進行詳細的闡述,其他部分暫且不談。webpack

2. 關於 Loader

Loader allow webpack to process other types of files and convert them into valid modules.git

Loader 的做用很簡單,就是處理任意類型的文件,而且將它們轉換成一個讓 webpack 能夠處理的有效模塊。github

2.1 Loader 的配置和使用

2.1.1 在 config 裏配置

Loader 能夠在 webpack.config.js裏配置,這也是推薦的作法,定義在 module.rules 裏:web

// webpack.config.js
module.exports = {
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader' },
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
          { loader: 'postcss-loader' },
        ]
      }
    ]
  }
};
複製代碼

每一條 rule 會包含兩個屬性:testuse,好比 { test: /\.js$/, use: 'babel-loader' } 意思就是:當 webpack 遇到擴展名爲 js 的文件時,先用 babel-loader 處理一下,而後再打包它。json

use 的類型:string|array|object|function

  • string: 只有一個 Loader 時,直接聲明 Loader,好比 babel-loader
  • array: 聲明多個 Loader 時,使用數組形式聲明,好比上文聲明 .cssLoader
  • object: 只有一個 Loader 時,須要有額外的配置項時。
  • function: use 也支持回調函數的形式。

關於 use 的多種配置方式,這裏就很少說了,能夠點擊 更多關於 use

注意:use 是經過數組形式聲明 Loader 時,Loader 的執行順序是從右到左,從下到上。好比暫且認爲上方聲明是這樣執行的:

postcss-loader -> css-loader -> style-loader

其實就是:

styleLoader(cssLoader(postcssLoader(content)))
複製代碼

爲何說是暫且呢,由於 style-loader 有點特殊,有興趣的看看這個 webpack loader 從上手到理解系列:style-loader

webpack 提供了多種配置 Loader 的方法,不過通常來講,use 就已經足夠用了,若是想了解更多,能夠點擊 更多關於 rule 的配置

2.1.2 內聯

能夠在 import 等語句裏指定 Loader,使用 ! 來將 Loader分開:

import style from 'style-loader!css-loader?modules!./styles.css';
複製代碼

內聯時,經過 query 來傳遞參數,例如 ?key=value

通常來講,推薦使用統一 config 的形式來配置 Loader,內聯形式多出現於 Loader 內部,好比 style-loader 會在自身代碼裏引入 css-loader

require("!!../../node_modules/css-loader/dist/cjs.js!./styles.css");
複製代碼

2.2 Loader 類型

2.2.1 同步 Loader

module.exports = function(source) {
  const result = someSyncOperation(source); // 同步邏輯
  return result;
}
複製代碼

通常來講,Loader 都是同步的,經過 return 或者 this.callback 來同步地返回 source轉換後的結果。

2.2.2 異步 Loader

有的時候,咱們須要在 Loader 裏作一些異步的事情,好比說須要發送網絡請求。若是同步地等着,網絡請求就會阻塞整個構建過程,這個時候咱們就須要進行異步 Loader,能夠這樣作:

module.exports = function(source) {
  // 告訴 webpack 此次轉換是異步的
  const callback = this.async();
  // 異步邏輯
  someAsyncOperation(content, function(err, result) {
    if (err) return callback(err);
    // 經過 callback 來返回異步處理的結果
    callback(null, result, map, meta);
  });
};
複製代碼

2.2.3 Pitching Loader

Pitching Loader 是一個比較重要的概念,以前在 style-loader 裏有提到過。

{
  test: /\.js$/,
  use: [
    { loader: 'aa-loader' },
    { loader: 'bb-loader' },
    { loader: 'cc-loader' },
  ]
}
複製代碼

咱們知道,Loader 老是從右到左被調用。上面配置的 Loader,就會按照如下順序執行:

cc-loader -> bb-loader -> aa-loader

每一個 Loader 都支持一個 pitch 屬性,經過 module.exports.pitch 聲明。若是該 Loader 聲明瞭 pitch,則該方法會優先於 Loader 的實際方法先執行,官方也給出了執行順序:

|- aa-loader `pitch`
  |- bb-loader `pitch`
    |- cc-loader `pitch`
      |- requested module is picked up as a dependency
    |- cc-loader normal execution
  |- bb-loader normal execution
|- aa-loader normal execution
複製代碼

也就是會先從左向右執行一次每一個 Loaderpitch 方法,再按照從右向左的順序執行其實際方法。

2.2.4 Raw Loader

咱們在 url-loader 裏和 file-loader 最後都見過這樣一句代碼:

export const raw = true;
複製代碼

默認狀況下,webpack 會把文件進行 UTF-8 編碼,而後傳給 Loader。經過設置 rawLoader 就能夠接受到原始的 Buffer 數據。

2.3 Loader 幾個重要的 api

所謂 Loader,也只是一個符合 commonjs 規範的 node 模塊,它會導出一個可執行函數。loader runner 會調用這個函數,將文件的內容或者上一個 Loader 處理的結果傳遞進去。同時,webpack 還爲 Loader 提供了一個上下文 this,其中有不少有用的 api,咱們找幾個典型的來看看。

2.3.1 this.callback()

Loader 中,一般使用 return 來返回一個字符串或者 Buffer。若是須要返回多個結果值時,就須要使用 this.callback,定義以下:

this.callback(
  // 沒法轉換時返回 Error,其他狀況都返回 null
  err: Error | null,
  // 轉換結果
  content: string | Buffer,
  // source map,方便調試用的
  sourceMap?: SourceMap,
  // 能夠是任何東西。好比 ast
  meta?: any
);
複製代碼

通常來講若是調用該函數的話,應該手動 return,告訴 webpack 返回的結果在 this.callback 中,以免含糊不清的結果:

module.exports = function(source) {
  this.callback(null, source, sourceMaps);
  return;
};
複製代碼

2.3.2 this.async()

同上,異步 Loader

2.3.3 this.cacheable()

有些狀況下,有些操做須要耗費大量時間,每一次調用 Loader 轉換時都會執行這些費時的操做。

在處理這類費時的操做時, webapck 會默認緩存全部 Loader 的處理結果,只有當被處理的文件發生變化時,纔會從新調用 Loader 去執行轉換操做。

webpack 是默承認緩存的,能夠執行 this.cacheable(false) 手動關閉緩存。

2.3.4 this.resource

當前處理文件的完整請求路徑,包括 query,好比 /src/App.vue?type=templpate

2.3.5 this.resourcePath

當前處理文件的路徑,不包括 query,好比 /src/App.vue

2.3.6 this.resourceQuery

當前處理文件的 query 字符串,好比 ?type=template。咱們在 vue-loader 裏有見過如何使用它:

const qs = require('querystring');

const { resourceQuery } = this;
const rawQuery = resourceQuery.slice(1); // 刪除前面的 ?
const incomingQuery = qs.parse(rawQuery); // 解析字符串成對象
// 取 query
if (incomingQuery.type) {}
複製代碼

2.3.7 this.emitFile

webpack 在輸出目錄新建一個文件,咱們在 file-loader 裏有見過:

if (typeof options.emitFile === 'undefined' || options.emitFile) {
  this.emitFile(outputPath, content);
}
複製代碼

更多的 api 可在官方文檔中查看:Loader Interface

3. Loader 工做流程簡述

咱們來回顧一下 Loader 的一些特色:

  • Loader 是一個 node 模塊;
  • Loader 能夠處理任意類型的文件,轉換成 webpack 能夠處理的模塊;
  • Loader 能夠在 webpack.config.js 裏配置,也能夠在 require 語句裏內聯;
  • Loader 能夠根據配置從右向左鏈式執行;
  • Loader 接受源文件內容字符串或者 Buffer
  • Loader 分爲多種類型:同步、異步和 pitching,他們的執行流程不同;
  • webpackLoader 提供了一個上下文,有一些 api 可使用;
  • ...

咱們根據以上暫時知道的特色,能夠對 Loader 的工做流程有個猜想,假設有一個 js-loader,它的工做流程簡單來講是這樣的:

  1. webpack.config.js 裏配置了一個 jsLoader
  2. 遇到 js 文件時,觸發了 js-loader;
  3. js-loader 接受了一個表示該 js 文件內容的 source;
  4. js-loader 使用 webapck 提供的一系列 apisource 進行轉換,獲得一個 result;
  5. result 返回或者傳遞給下一個 Loader,直處處理完畢。

webpack 的編譯流程很是複雜,暫時還不能看明白而且梳理清楚,在這裏就不誤導你們了。

關於 Loader 的工做流程以及源碼分析能夠看 【webpack進階】你真的掌握了loader麼?- loader十問

4. 如何編寫一個 Loader

雖然咱們對於 webpack 的編譯流程不是很熟悉,可是咱們能夠試着編寫一個簡單功能的 Loader,從而加深對 Loader 的理解。

4.1 Loader 用法準則

編寫 Loader 時須要遵循一些準則,官方有很詳細的文檔,就不重複闡述了。點擊 Loaders 用法準則 查看。

這裏說一下單一任務和鏈式調用

一個 Loader 應該只完成一個功能,若是須要多步的轉換工做,則應該編寫多個 Loader 來進行鏈式調用完成轉換。好比 vue-loader 只是處理了 vue 文件,起到一個分發的做用,將其中的 template/style/script 分別交給不一樣的處理器來處理。

這樣會讓維護 Loader 變得更簡單,也能讓不一樣的 Loader 更容易地串聯在一塊兒,而不是重複造輪子。

4.2 Loader 工具庫

編寫 Loader 的過程當中,最經常使用的兩個工具庫是 loader-utilsschema-utils,在如今常見的 Loader 中都能看到它們的身影。

4.2.1 loader-utils

它提供了許多有用的工具,但最經常使用的一種工具是獲取傳遞給 Loader 的選項:

import { getOptions } from 'loader-utils';

export default function loader(src) {
  // 加載 options
  const options = getOptions(this) || {};
}
複製代碼

loader-utils

4.2.2 schema-utils

配合 loader-utils,用於保證 Loader 選項,進行與 JSON Schema 結構一致的校驗。

import validateOptions from 'schema-utils';
import schema from './options.json';

export default function loader(src) {
  // 校驗 options
  validateOptions(schema, options, {
    name: 'URL Loader',
    baseDataPath: 'options',
  });
}
複製代碼

schema-utils

更多關於如何編寫一個 Loader傳送門

5. 總結

本文對 webpackLoader 相關知識點進行整理和概括,正在學習中,若有不足歡迎指出。

相關文章
相關標籤/搜索