面試官問懵了?Webpack 的 Loader + Plugin開發(fā)竟比配置還簡單?
在前兩天同學(xué)的面試中,有一位同學(xué)被問到 如何開發(fā) webpack 的 loader 和 plugin?有沒有實際 loader 或者 plugin 的開發(fā)經(jīng)驗。果然,面試只會越來越卷啊。
webpack
是大家所熟知的打包工具,里面包含了 5 個核心概念:
- 入口:entry
- 出口:output
- 加載器:loader
- 插件:plugin
- 模式:mode
圖片
入口、出口、模式 的概念其實都比較好理解。但是一旦涉及到 loader
和 plugin
,特別是實現(xiàn) loader
和 plugin
很多小伙伴就比較懵了。
所以,今天咱們就拿出 10 分鐘的時間,一起來看那看那 loader
和 plugin
是如何實現(xiàn)的!
module、chunk、bundle
如果要講解 plugin
和 loader
那么會涉及到三個術(shù)語:module、chunk、bundle
。
所以咱們就先說明下 module、chunk、bundle
然后再來看下 plugin
和 loader
。
在打包工具中,有三個 “術(shù)語”:module、chunk、bundle
:
module:模塊
:通常一個模塊代表了一個文件。一般指的是js
文件,當(dāng)然也可以是css 文件
或者 圖像文件。chunk:塊
:塊通常是在構(gòu)建過程中由打包工具(如:Webpack
)根據(jù)配置生成的,它們由一組相關(guān)的模塊放在一起打包組成。bundle:打包文件
:打包文件是指構(gòu)建工具在打包過程中生成的最終輸出文件,可以在瀏覽器中加載并運行。
總的來說:模塊是組成項目的一個個文件,塊是由一組相關(guān)模塊組成的單元,而打包文件是構(gòu)建工具最終生成的包含模塊和資源的輸出文件。
實現(xiàn) loader 和 plguin
那么明確好了這三個基本的概念之后,接下來咱們來看下 loader
和 plugin
:
loader
一般被稱為 加載器, webpack
默認只能處理 .js
的文件。如果項目中遇到其他類型的文件,那么就需要通過 loader
進行處理。
plugin
一般被叫做 插件,它可以為 構(gòu)建工具(不只是 webpack
,還包含 vite
或者 rollup
) 提供一些附加的功能。比如說,咱們上一小節(jié)用到了 HtmlWebpackPlugin
它就是一個典型的插件,它可以在 webpack
構(gòu)建的過程中生成一個新的 HTML
文件。并且自動生成新的 bundle
文件。
明確好了它們的基本概念之后,接下來咱們來看下它們 運行時機 的區(qū)別,咱們來看下面這張圖:
圖片
從圖中咱們可以看到:
loader
是在打包之前執(zhí)行的,執(zhí)行的時機比較固定。其實也很好理解嘛。loader
它實質(zhì)就是一個轉(zhuǎn)換器,將A
文件進行編譯形成B
文件,操作的是文件,比如:將A.scss
轉(zhuǎn)變?yōu)?/span>B.css
,單純的文件轉(zhuǎn)換過程。- 而
plugin
是在整個編譯的周期中都會起作用。webpack
在整個運行的生命周期中,會廣播出很多的事件,plugin
就可以監(jiān)聽這些事件,然后在某一個時機下,改變輸出結(jié)果就可以了。
那么明確好了它們的一個運行機制之后,接下來咱們來實現(xiàn)一個簡單的 loader
和 plugin
:
loader 的簡單實現(xiàn)
需求:
實現(xiàn)一個
loader
處理txt
文件,把hello world
轉(zhuǎn)化為 配置對象下content
屬性的值
- 在
webpack-project/vue.config.js
中利用chainWebpack
添加新的loader
:
// 添加一個處理 txt 文件的loader
config.module
// 創(chuàng)建一個新的規(guī)則,命名為 'custom-loader'
.rule('txt-loader')
// 適用于哪些文件
.test(/\.txt$/)
// 指定要使用的 loader 的名稱
.use('txt-loader')
// loader 的路徑
.loader('./src/loaders/textLoader')
// 配置對象
.options({
content: '你好,世界'
})
.end()
- 然后創(chuàng)建
test.txt
文件:
hello world
- 實現(xiàn)
textLoader
:
const loaderUtils = require('loader-utils')
// 接收options配置
module.exports = function (source) {
// 獲取配置文件
const options = loaderUtils.getOptions(this)
// 把 hello world 替換成 content 屬性配置
source = source.replace(/hello world/, options.content)
// 最后需要返回一個可執(zhí)行的代碼,所以需要 module.exports = '內(nèi)容'
return `module.exports = '${source}'`
}
- 最后在
main.js
中導(dǎo)入該文件,并打?。?/span>
const text = require('./test.txt')
console.log(text)
plugin 的簡單實現(xiàn)
看完 loader
之后,接下來咱們來看一個 plugin
的構(gòu)建。
需求:
在
webpack
打印完成之后,在終端輸出指定內(nèi)容
- 創(chuàng)建
webpack-project/src/plugins/logPlugin.js
文件:
class LogPlugin {
// 通過構(gòu)造函數(shù),獲取到傳入的內(nèi)容 content
constructor(options) {
this.content = options.content
}
// Webpack 會調(diào)用 logPlugin 實例的 apply 方法給插件實例傳入 compiler 對象。compiler 表示編譯器的實例,它代表了完整的 webpack 環(huán)境配置
apply(compiler) {
// 指定一個掛載到 webpack 自身的事件鉤子。done 會在 webpack 構(gòu)建完成后回調(diào)
compiler.hooks.done.tap('logPlugin', (compilation) => {
// compilation: 當(dāng)前打包構(gòu)建流程的上下文
console.log(this.content)
})
}
}
module.exports = LogPlugin
- 在
vue.config.js
中,添加plugin
:
// 添加一個新的 plugin
// 添加一個新的插件
config
.plugin('LogPlugin')
.use(LogPlugin, [{ content: 'hello sunday' }])
.end()
此時,運行項目可在終端打印指定內(nèi)容。
webpack
在官網(wǎng)中提供了 如何構(gòu)建 loader 和 如何構(gòu)建 plugin 的文檔,大家如果想要深入了解它們的構(gòu)建方式的話,那么可以查詢下對應(yīng)的文檔內(nèi)容。