重拾 Webpack(中卷)

Ozzie發表於2019-12-17

之前在上卷中,我主要梳理了 webpack 入門知識,在此我將更接近於工程中的知識實際梳理一遍

本文持續更新中…
前置知識:重拾 Webpack(上卷)


前處理器(loader)

loader 概述

  1. 每一個 loader 本質上都是一個函式,在 webpack4 之前,loader 函式的輸入輸出都必須為字串,但在 webpack4 之後,loader 也同時支援 抽象語法樹(AST) 的傳遞,以此來減少程式碼的重複解析
    用公式可以表示為:output = loader(input)
  2. 在此,我們來看一下 loader 的原始碼結構,以此闡釋 loader 是如何工作的:
     module.exports = function loader (content, map, meta) {
         var callback = this.async();
         var result = handler(content, map, meta);
         callback{
             null,  // error
             result.content,  // 轉換後的內容
             result.map,  // 轉換後的 source-map
             result.meta,  // 轉換後的 AST
         };
     };
    從程式碼中可以看出,loader 本身就是一個函式,將函式中接收到的內容進行轉換,然後返回轉換後的結果
    (可能包含 sourcr map 和 AST 物件

loader 的配置

  1. webpack 只認識 JavaScript,對於其他型別的資源,比如 CSS、圖片等等,必須預先定義一個或多個 loader 來進行轉譯,經過 loader 輸出為 webpack 能夠接受的形式再繼續進行
    因此,loader 做的實際上是一個預處理的工作
  2. loader 的引入:
    • 假設在我們的目錄中,有以下兩種檔案:
      |-app.js
      |-style.css
    • 我們要做的是將 css 檔案引入到 js 檔案中使用,那麼跟著我開始吧!
    • 安裝第三方模組
      • webpack 本身並不含有任何的 loader,而所有的 loader 都是第三方 npm 模組,所以我們必須先安裝它
        npm install --save-dev css-loader
    • 引入到工程中
      • webpack.config.js 中配置:
        module.exports = {
          // ...
          module: {
                  rules: [{
                      test: /\.css$/,
                      use: ['css-loader'],
                  }],
              },
        };
    • 配置項的說明:
      • module.rules 代表著模組的處理規則,每條規則都包含著許多配置項,這裡我們只用最重要的兩項:test 和 use
      • test 接受一個正規表示式或者元素為正規表示式的陣列,如果匹配這條正規表示式,那麼就會使用與之對應的規則,比如這裡的 /\.css$/ 用來匹配所有的 .css 結尾的檔案
      • use 接受一個陣列,陣列中包含著對應 test 會使用到的所有 loader,比如這裡的 css 檔案會使用到 css-loader ,如果只有一個 loader,那麼也可以省略陣列,只寫成字串
  3. 鏈式 loader:
    • 上述的案例是不成功的,因為我們還差一樣東西:style-loader
    • 每一個 loader 的功能都是獨特而侷限的,也就是說,一種型別的模組可以需要多種 loader 共同完成,在處理 css 檔案時同樣如此,需要 style-loadercss-loader 共同完成
    • 安裝
      npm install --save-dev style-loader
    • 新增配置
      // webpack.config.js
      module.exports = {
        // ...
        module: {
                rules: [{
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader'],
                }],
            },
      };
      在 webpack 打包時,是按照陣列從後往前的順序將資源交給 loader 處理的,因此要把最後生效的 style-loader 放在最前面
  4. exclude 與 include
    • 在專案中我們經常會用到 babel-loader 來處理 ES6+ 語言特性,將其編譯為 ES5 來適應瀏覽器,但是對於 node_modules 中的 js 檔案來說,很多都是已經編譯為 ES5 的,因此沒必要再使用 babel-loader 來進行額外的處理
    • exclude 與 include 就是用來排除規則的,他們可接收正規表示式或者字串(字串即檔案的絕對路徑),以及由他們組成的陣列,如:
      rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader'],
            exclude: /node_modules/,
      }],
      • 這裡的含義是:對於匹配到正規表示式的模組,若在 exclude 指定的目錄中(此處為 node_modules 目錄),則不會執行該 use 規則
    • 除了 exclude 之外,使用 include 配置也能達到相同的效果,如:
      rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader'],
            include: /src/,
      }],
      • 值得注意的是,include 的邏輯似乎與 exclude 是相反的,對於匹配到正規表示式的模組,若在 include 指定的目錄中(此處為 src 目錄),才執行該 use 規則
    • exclude 與 include 同時存在時,exclude 的優先順序更高,如:
      rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader'],
            exclude: /node_modules/,
            include: /node_modules\/awesome/,
      }]
      • 此時,exclude 使得 node_modules 目錄已經被排除了,雖然 include 之後想要讓該 use 規則對 node_modules 中的某一個模組生效,也是無濟於事的,因為 include 是無法覆蓋 exclude 的
      • 要實現原本的需求,我們可以改為:
        rules: [{
              test; /\.css/,
              use: ['style-loader', 'css-loader'],
              // 用 exclude 排除 node_modules 中除了 foo 和 bar 以外的所有模組
              exclude: /node_modules\/(?!(foo|bar)\/).*/,
        }]
        這令人頭疼的正則,哎,,Ծ^Ծ,,
      • 另外,由於 exclude 的優先順序跟高,所以我們可以對 include 的子目錄進行排除,如:
        rules: [{
              test; /\.css/,
              use: ['style-loader', 'css-loader'],
              exclude: /src\/lib/,
              include: /src/,
        }]
        此處的結果就是:該 use 規則只對 src 目錄生效,同時排除 src 目錄中的 lib 目錄
    • 注:exclude 與 include 的配置項往往是必須考慮的,也是常加的,否則可能會拖慢整體的打包速度
  5. resource 與 issuer
    • resource 與 issuer 可以用於更加精確地確定模組規則的作用範圍,如:
      // index.js
      import './style.css'
      在 webpack 中,被載入模組是 resource,而載入者是 issuer,在上面的例子中,resource 是 style.css,載入者是 index.js
    • 前面介紹的 test、exclude、include 在本質上就是對 resource 的配置,如果想要對 issuer 增加條件的話,需要額外地寫配置,比如,我們只想讓 /src/pages 目錄下的 js 檔案可以引用 css,本質想就是改變載入者的範圍,配置如下:
      rules: [{
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /node_modules/,
        issuer: {
                test: /\.js$/,
                include: /src/pages/,
            },
      }],
      // 只有 /src/pages/ 下面的 js 檔案引用 css 檔案才能使此 use 規則生效
      • 事實上,上述程式碼的可讀性較差,我們可以改為:
        rules: [{
              use: ['style-loader', 'css-loader'],
              resource: {
                      test: /\.css$/,
                      exclude: /node_modules/,
                  },
              issuer: {
                      test: /\.js$/,
                      exclude: /node_modules/,
                  },
        }],
        注:此風格和上面的程式碼風格無法並存的,只能選擇一種風格配置
  6. enforce
    • webpack 中的 loader 按照執行順序可分為:pre、inline、normal、post四種,之前我們直接定義的 loader 都屬於 normal 型別,官方已不推薦 inline 形式,而 pre 和 post 形式則需我們使用 enforce 指定,如:
      rules: [{
            test: /\.css$/,
            enforce: 'pre',
            use: 'eslint-loader',
            // eslint-loader 的功能是對原始碼進行質量檢測,這種檢測工作往往在其他 loader 之前執行
      }]
      這段程式碼的解釋為:對於所有匹配正規表示式的模組,首先使用的 loader 規則就是 eslint-loader
    • 概述起來就是,enforce 可以強制指定 loader 的執行順序
      • pre 能強制 loader 首先執行
      • post 能強制 loader 最後執行
  7. 常用 loader 介紹
    • loader 官方文件
    • 模板 loader:
      • html-loader : 將HTML檔案匯出編譯為字串,可供js識別的其中一個模組
      • pug-loader : 載入pug模板
      • jade-loader : 載入jade模板(是pug的前身,由於商標問題改名為pug)
      • ejs-loader : 載入ejs模板
      • handlebars-loader : 將Handlebars模板轉移為HTML
    • 樣式 loader:
      • css-loader : 解析css檔案中程式碼
      • style-loader : 將css模組作為樣式匯出到DOM中
      • less-loader : 載入和轉義less檔案
      • sass-loader : 載入和轉義sass/scss檔案
      • postcss-loader : 使用postcss載入和轉義css/sss檔案
    • 指令碼轉換編譯 loader:
      • script-loader : 在全域性上下文中執行一次javascript檔案,不需要解析
      • babel-loader : 載入ES6+ 程式碼後使用Babel轉義為ES5後瀏覽器才能解析
      • typescript-loader : 載入Typescript指令碼檔案
      • coffee-loader : 載入Coffeescript指令碼檔案
    • JSON載入 loader:
      • json-loader : 載入json檔案(預設包含)
      • json5-loader : 載入和轉義JSON5檔案
    • Files檔案 loader:
      • raw-loader : 載入檔案原始內容(utf-8格式)
      • url-loader : 多數用於載入圖片資源,超過檔案大小顯示則返回data URL
      • file-loader : 將檔案傳送到輸出的資料夾並返回URL(相對路徑)
      • jshint-loader : 檢查程式碼格式錯誤
    • 載入框架 loader:
      • vue-loader : 載入和轉義vue元件
      • angualr2-template–loader : 載入和轉義angular元件
      • react-hot-loader : 動態重新整理和轉義react元件中修改的部分,基於webpack-dev-server外掛需先安裝,然後在webpack.config.js中引用react-hot-loader

樣式處理

正在更新中…

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章