首頁 > 軟體

style-loader為什麼要使用pitch方法原理解析

2023-03-02 18:00:20

loader

loader是一個函數,loader模組要預設匯出該函數,同時這個函數上可以有pitch方法,webpack會執行這個pitch方法,pitch方法會影響webpack後續行為。

loader的作用是將原始檔轉化為可以執行的js模組,webpack會檢查loader返回的這個模組是否是正確的,符合js模組化規範,如果有錯誤會終止打包。例如我定義了一個test.js模組,其預設匯出一個函數:

test.js

module.exports = function (a, b) {
    return a+b;
}

然後定義一個test-loader,讓其只會匹配test.js

module.exports = function (content) {
    return `var a = {name: 'wjl'}; module.exports = a;`
}

test-loader返回了一段新的程式碼,預設匯出一個物件,在index.js中我們匯入test.js模組,嘗試輸出物件上的name屬性,然後通過webpack打包:

const a = require('./test.js')
console.log(a.name)

打包結果為main.js,執行main.js可以正確得到輸出。這說明webpack執行了var a = {name: 'wjl'}; module.exports = a;test.js模組最終匯出的內容為a

當把test-loader的匯出語句刪除,改為以下內容時,能夠正確通過webpack檢查,但是index.js中不能存取匯出物件了:

test-loader.js

module.exports = function (content) {
    // return `var a = {name: 'wjl'}; console.log(123); module.exports = a;`
    return `var a = {name: 'wjl'}; console.log(123);`
}

index.js

const a = require('./test.js')
console.log(a, a.name)

執行匯出檔案main.js得到

這是由於這個模組沒有匯出內容(物件上沒有屬性),test.js的模組程式碼會在執行時執行(輸出123,cjs的模組需要執行完模組內容才能得到匯出物件)。

(如果loader匯出的內容中含有importrequire等語句,webpack會再次進行相關內容的匯入,這方面的知識目前暫時不分析)

loader總結loader的作用是將準備匯入的模組裡面的內容轉換成可以正常執行的js模組程式碼,轉換後的內容會在執行時執行,以得到模組的匯出內容或執行其他副作用程式碼。

pitch

為什麼需要使用pitch?我們以css-loaderstyle-loader作為分析。

在只使用css-loader的情況下,假設我們有兩個檔案:index.cssindex.jsindex.css定義了一些樣式,index.js匯入了index.css

index.css

body  {
    font-size: 16px;
    color: red;
}

index.js

const style = require('./index.css')
console.log(style);

執行結果為:

css-loader將目標樣式檔案轉換成一個js物件並匯出了該物件,預設屬性上有index.css檔案的資訊。

需要注意的是,這個物件是執行完css-loader轉換的模組內容後得到的!我們先定義一個普通的my-style-loader根據呼叫順序拿到css-loader的返回值:

my-style-loader:

module.exports = function (source) {
  console.log('*******************');
  console.log(source);
  console.log('*******************');
  return source;
}

css-loaderindex.css模組轉換為以下內容:

// Imports
import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from "./node_modules/.pnpm/registry.npmmirror.com+css-loader@6.7.3_webpack@5.75.0/node_modules/css-loader/dist/runtime/noSourceMaps.js";
import ___CSS_LOADER_API_IMPORT___ from "./node_modules/.pnpm/registry.npmmirror.com+css-loader@6.7.3_webpack@5.75.0/node_modules/css-loader/dist/runtime/api.js";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___);
// Module
___CSS_LOADER_EXPORT___.push([module.id, "body  {rn    font-size: 16px;rn    color: red;rn}", ""]);
// Exports
export default ___CSS_LOADER_EXPORT___;
​

也就是說,index.css裡面的內容,還是要執行完index.css模組才能得到

使用style-loader的目的是往document中插入style標籤,如果style-loader是一個普通loader的話,它需要執行css-loader返回的模組才能得到css樣式,當然可以解析css-loader返回的模組內容,然後得到樣式,然後返回建立style標籤的相關語句,但是這樣工作量太大了。而返回的模組中有import相關的語句時,webpack還會載入那些import的內容,但是例如./node_modules/.pnpm/registry.npmmirror.com+css-loader@6.7.3_webpack@5.75.0/node_modules/css-loader/dist/runtime/api.js是會被webpack最終打包生成的模組,在nodejs環境中是無法得到的。

打包產物:

一個模組載入(import '!!xxxx-loader!./index.css')被webpack打包之後會新增到module map裡面,鍵就是請求路徑。

style-loader的思路就是,得到css-loader的模組內容,然後再將模組內容插入到style標籤中,再將style標籤插入檔案中。

為了得到css-loader處理後的內容(需要能被執行),style-loader構造了一個新的require語句,即:

require(`${loaderUtils.stringifyRequest(this, '!!' + remainingRequest)}`)
// !!./node_modules/.pnpm/registry.npmmirror.com+css-loader@6.7.3_webpack@5.75.0/node_modules/css-loader/dist/cjs.js!./index.css

webpack發現返回的內容中有模組匯入,然後使用路徑中的loader去載入這個模組,並將其儲存在記憶體中(多個檔案參照同一個模組,目標模組只會被處理一次)。

總結

loaderpicher本質上都是改變目標檔案的內容,讓它變成符合js語法的程式碼,如果返回的內容有不存在的匯入,則會再次執行匯入。

webpack每個loader處理的結果都會生成單獨的模組,但是在loader函數中,它無法之前使用了哪些loader,也無法知道已生成模組的名字,因此style-loader無法在loader函數中匯入已經生成的模組。

pitch階段可以獲取到之後的loader順序,實現起來也更加方便。

以上就是style-loader為什麼要使用pitch方法原理解析的詳細內容,更多關於style-loader使用pitch方法的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com