<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【webpack】loader 知識分享

          共 10436字,需瀏覽 21分鐘

           ·

          2021-04-21 22:37

          什么是 loader

          webpack 只能理解 JavaScript 和 JSON 文件,這是 webpack 開箱可用的自帶能力。loader 讓 webpack 能夠去處理其他類型的文件,并將它們轉(zhuǎn)換為有效 模塊[1],以供應(yīng)用程序使用,以及被添加到依賴圖中。loader 本質(zhì)上是導(dǎo)出為函數(shù)的 JavaScript 模塊。—— webpack 官方中文文檔

          如下圖所示,在 webpack 使用過程中,經(jīng)常會出現(xiàn)以下兩種形式,前者更多是我們在 webpack 配置文件中,去根據(jù)文件匹配信息,去配置 loader 相關(guān)信息;后者更多是在 loader / plugin 中去修改/替換/生成的行內(nèi) loader 信息。這就涉及到 loader 的相關(guān)分類。

          // webpack.config.js
          {
            module: {
              rules: [
                {
                  test/.txt$/,
                  use: [
                    {
                      loader: getLoader("a-loader.js"),
                    },
                  ],
                  enforce"pre",
                },
                {
                  test/.txt$/,
                  use: [
                    {
                      loader: getLoader("b-loader.js"),
                    }
                  ],
                  enforce"post",
                },
              ],
            },
          }
          // app.js
          import "/Users/jiangyuereee/Desktop/loader/d-loader.js!./txt.txt"

          loader 的分類

          在 webpack 里,loader 可以被分為四類,分別是:后置post,普通normal,行內(nèi)inline,前置pre

          enforce

          對于postnormalpre,主要取決于在配置里Rule.enforce的取值:pre || post,若無設(shè)置,則為normal

          注意:相對于的是 Rule,并非某個 loader。那么作用于的就是對應(yīng) Rule 的所有 loader。

          inline

          行內(nèi) loader 比較特殊,是在import / require的時候,將 loader 寫入代碼中。而對于inline而言,有三種前綴語法:

          • !:忽略normalloader
          • -!:忽略preloader 和normalloader

          • !!:忽略所有 loader(pre / noraml / post

          行內(nèi) loader 通過!將資源中的 loader 進(jìn)行分割,同時支持在 loader 后面,通過?傳遞參數(shù),參數(shù)信息參考 loader.options 內(nèi)容。

          而以上說的三種前綴語法,則是寫在內(nèi)聯(lián) loader 字符串的前綴上,來表示忽略哪些配置 loader

          example

          a-loaderpre loaderb-loadernormal loaderc-loaderpost loader為例。

          補充:本文的 loader 均為

          module.exports = function (content{
            console.log("x-loader");
            return content;
          };

          module.exports.pitch = function (remainingRequest, precedingRequest, data{
            console.log("x-loader-pitch");
          };
          • 無前綴信息
          import "/Users/jiangyuereee/Desktop/loader/d-loader.js!./txt.txt"

          c-loader-pitch
          d-loader-pitch
          b-loader-pitch
          a-loader-pitch
          a-loader
          b-loader
          d-loader
          c-loader
          • !前綴信息
          import "!/Users/jiangyuereee/Desktop/loader/d-loader.js!./txt.txt";

          c-loader-pitch
          d-loader-pitch
          a-loader-pitch
          a-loader
          d-loader
          c-loader
          • -!前綴信息
          import "-!/Users/jiangyuereee/Desktop/loader/d-loader.js!./txt.txt";

          c-loader-pitch
          d-loader-pitch
          d-loader
          c-loader
          • !!前綴信息
          import "!!/Users/jiangyuereee/Desktop/loader/d-loader.js!./txt.txt";

          d-loader-pitch
          d-loader

          loader 的優(yōu)先級

          四種 loader 調(diào)用先后順序為:pre > normal > inline > post

          在相同種類 loader 的情況下,調(diào)用的優(yōu)先級為,自下而上,自右向左。(pitch 情況下,則反過來)。

          例如:

          {
            module: {
              rules: [
                {
                  test/.txt$/,
                  use: [
                    {
                      loader: getLoader("a-loader.js"),
                    },
                  ],
                  enforce"post",
                },
                {
                  test/.txt$/,
                  use: [
                    {
                      loader: getLoader("b-loader.js"),
                    },
                    {
                      loader: getLoader("c-loader.js"),
                    },
                  ],
                  enforce"post",
                },
              ],
            },
          }
          a-loader-pitch
          b-loader-pitch
          c-loader-pitch
          c-loader
          b-loader
          a-loader

          Loader 調(diào)用鏈

          每個 loader 都有自己的 normal 函數(shù)和 pitch 函數(shù),而調(diào)用過程則是先根據(jù)從低到高的優(yōu)先級順序,調(diào)用 loader 各自的 pitch 函數(shù),再由高到低調(diào)用各自的 normal 函數(shù),其過程,更像是一個洋蔥模型。

          Loader - pitch

          loader 總是 從右到左被調(diào)用。有些情況下,loader 只關(guān)心 request 后面的 元數(shù)據(jù)(metadata),并且忽略前一個 loader 的結(jié)果。在實際(從右到左)執(zhí)行 loader 之前,會先 從左到右 調(diào)用 loader 上的 pitch 方法。—— webpack 官方中文文檔

          對于一個 loader,除了通過module.exports導(dǎo)出處理函數(shù)外,還可以通過module.exports.pitch導(dǎo)出pitch方法。正常來說,在loader從右向左調(diào)用之前,會進(jìn)行一次從左到右的pitch方法調(diào)用,而在pitch調(diào)用過程中,如果任何一個有返回值,那么將阻斷后續(xù)的loader調(diào)用鏈,進(jìn)而將自身的返回結(jié)果傳遞給上一個loader作為content

          補充:pitch方法同樣有同步 / 異步之分,同樣可以選擇return或者this.callback去傳遞更多的信息,后續(xù)會講到。

          loader 的調(diào)用過程:

          (圖片來源于網(wǎng)絡(luò))

          而一旦其中的一個pitch返回了結(jié)果,那么將跳過后續(xù)的loader,將返回結(jié)果傳遞給前一個loader

          (圖片來源于網(wǎng)絡(luò))

          像常見的 style-loader / vue-loader 等等,就會利用 pitch 階段進(jìn)行攔截處理工作,從而實現(xiàn) loader 特色化工作。

          同步 / 異步 loader

          如果是單個處理結(jié)果,可以在 同步模式 中直接返回。如果有多個處理結(jié)果,則必須調(diào)用 this.callback()。在 異步模式 中,必須調(diào)用 this.async()來告知 loader runner[2] 等待異步結(jié)果,它會返回 this.callback() 回調(diào)函數(shù)。隨后 loader 必須返回 undefined 并且調(diào)用該回調(diào)函數(shù)。—— webpack 官方中文文檔

          在 webpack 中,loader 可能會由于依賴于讀取外部配置文件、進(jìn)行網(wǎng)絡(luò)請求等等原因,從而比較耗時。而此時如果進(jìn)行同步操作,就會導(dǎo)致 webpack 阻塞住,所以 loader 會有同步 / 異步之分。

          在 loader 中,可以通過兩種方式返回數(shù)據(jù):

          • returnreturn只能返回content信息;
          • callback
          this.callback(
            err: Error | null,    // 錯誤信息
            content: string | Buffer,    // content信息
            sourceMap?: SourceMap,    // sourceMap
            meta?: any    // 會被 webpack 忽略,可以是任何東西(例如:AST、一些元數(shù)據(jù)啥的)。
          );

          同步 loader

          對于同步 loader 而言,使用return或者this.callback均可以達(dá)到想要的效果。只是說,相對于returnthis.callback可以返回更多的信息。

          module.exports = function(content, map, meta{
            // return handleData(content);
            this.callback(null, handleData(content), handleSourceMap(map), meta);
            return// 當(dāng)調(diào)用 callback() 函數(shù)時,總是返回 undefined
          };

          異步 loader

          對于異步 loader 而言,需要通過this.async(),來獲取到callback函數(shù)。

          module.exports = function(content, map, meta{
            var callback = this.async();
            asycnHandleData(content, function(err, result{
              if (err) return callback(err);
              callback(null, result, map, meta);
            });
          };

          loader 的參數(shù)

          一般來說,起始 loader 只有一個入?yún)ⅲ嘿Y源文件的內(nèi)容。默認(rèn)情況下,資源文件的內(nèi)容會以 UTF-8 字符串傳遞給 loader。而在有需要的情況下,loader 可以通過設(shè)置module.exports.raw = true;來表示,需要接受一個Buffer。同時,每個 loader 都可以傳遞String || Buffer,complier 會將其在loader之間根據(jù)raw的值來進(jìn)行轉(zhuǎn)換。(例如:圖片文件等)

          而根據(jù)callback的參數(shù)也可以知道,除了起始 loader 之外,loader 可以接受三個參數(shù):contentsourceMapmeta

          loader.pitch 的參數(shù)

          pitch 一共有三個參數(shù):

          • remainingRequest:當(dāng)前 loader 右側(cè)的所有 loader 加上資源路徑,根據(jù)!分割,連接而成的內(nèi)聯(lián) loader。
          • precedingRequest:當(dāng)前 loader 左側(cè)的所有 loader,根據(jù)!分割,連接而成的內(nèi)聯(lián) loader。

          • data:在 pitch 階段和 normal 階段之間共享的 data 對象。即:pitch 階段的參數(shù) data 和 normal 階段通過 this.data 獲取的 data 為同一對象。

          補充:左右側(cè),相對于 loader 調(diào)用鏈,normal 階段。

          example

          例如:有abc三個 loader,( use: [a, b, c] )在處理某個文件的時候,對應(yīng)的 a、b、c 各自的 pitch 參數(shù)分別為:

          • a:remainingRequest:b-loader.js!c-loader.js!file.txt;precedingRequest: '';
          • b:remainingRequest:c-loader.js!file.txt;precedingRequest: a-loader.js;

          • c:remainingRequest:file.txt;precedingRequest: a-loader.js!b-loader.js;

          loader 的輸出

          compiler 預(yù)期得到最后一個 loader 產(chǎn)生的處理結(jié)果。這個處理結(jié)果應(yīng)該為 String 或者 Buffer(能夠被轉(zhuǎn)換為 string)類型,代表了模塊的 JavaScript 源碼。另外,還可以傳遞一個可選的 SourceMap 結(jié)果(格式為 JSON 對象)。—— webpack 官方中文文檔

          當(dāng) loader 鏈路到了最后一個loader時,compiler期望得到的處理結(jié)果是可轉(zhuǎn)換為StringBuffer或者String類型,表示當(dāng)前模板處理后的js源碼。同時根據(jù)callback函數(shù)可知,還可以傳遞一個sourceMap

          除去正常 loader 處理返回的形式輸出源碼外,還可以根據(jù)this.emitFile來進(jìn)行額外輸出文件。

          emitFile(
              name: string,
              content: Buffer|string,
              sourceMap?: {...}
          );

          loader 緩存

          開發(fā)環(huán)境默認(rèn)情況下,loader 的處理結(jié)果會被標(biāo)記為可緩存。因為很多 loader 轉(zhuǎn)換的過程是非常耗時的,webpack 默認(rèn)會將所有 loader 標(biāo)記為可緩存的,在依賴文件沒有改變的情況下是不會重新進(jìn)行 loader 處理的過程。

          可以通過this.cacheable=false來標(biāo)記 loader 為不可緩存,在大部分情況下,還是不推薦將 loader 標(biāo)記為不可緩存,可以使用this.addDependency添加文件依賴。

          example

          {
            entry"./app.js",
            mode"development",
            module: {
              rules: [
                {
                  test/.txt$/,
                  use: [
                    {
                      loader: getLoader("a-loader.js"),
                    },
                  ],
                },
              ]
            }
          }

          在.txt 文件沒有發(fā)生變化的時候,watch 模式下,重新編譯是不會  重新調(diào)用 a-loader 去重新處理.txt 文件。如果期望在某個.js 文件發(fā)生變化的時候,重新調(diào)用 a-loader 進(jìn)行處理,就可以使用 this.addDependency 添加文件依賴。

          module.exports = function (content{
            console.log("a-loader");

            this.addDependency("/Users/jiangyuereee/Desktop/loader/c.js");
            return content;
          };

          注意:整個 loader 調(diào)用鏈都會被重新激活。

          loaderAPI

          除了以上提到的一些 API 之外,在 loader 內(nèi)還可以使用一些其他 API:The Loader Context[3]

          loader 開發(fā)工具包

          1. Loader-runner[4]

          可以通過該包進(jìn)行 loader 的開發(fā)和調(diào)試。

          1. Loader-utils[5]

          集成了 loader 開發(fā)中,常用的一些方法,方便開發(fā)。

          1. Schema-utils[6]

          便于驗證 loader options 的合法性(包括但不限于 loader 使用)。


          參考資料

          [1]

          模塊: https://webpack.docschina.org/concepts/modules

          [2]

          loader runner: https://github.com/webpack/loader-runner

          [3]

          The Loader Context: https://webpack.docschina.org/api/loaders/#the-loader-context

          [4]

          Loader-runner: https://github.com/webpack/loader-runner

          [5]

          Loader-utils: https://github.com/webpack/loader-utils#readme

          [6]

          Schema-utils: https://github.com/webpack/schema-utils

          [7]

          webpack 官方中文文檔: https://webpack.docschina.org/concepts/loaders/

          [8]

          Loader 機制: https://tsejx.github.io/webpack-guidebook/principle-analysis/implementation-principle/loader

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕+乱码+中文乱码91 | 亚洲视频手机在线播放 | 91欧美 | 成人午夜做爱 | 超碰青娱乐91 |