前言
最近在網(wǎng)上看到的一些優(yōu)秀的webpack的面試總結(jié):
「吐血整理」再來一打Webpack面試題
淺談 webpack 性能優(yōu)化(內(nèi)附 webpack 學習筆記)
揭秘webpack plugin
webpack 的 loader 和 plugin 你真的弄懂了嗎
Webpack HMR 原理解析
根據(jù)以上文檔惫东,自己總結(jié)一份簡陋版的webpack筆記
webpack構(gòu)建流程
-
初始化參數(shù)
:從配置文件webpack.config.js和shell語句中讀取與合并,得到最終參數(shù)options赐俗。 -
開始編譯
:用步驟1得到的參數(shù)options初始化Compiler對象朝抖,加載并配置所有插件歪赢,執(zhí)行對象的run方法。 -
確定入口
:根據(jù)配置的entry找出所有入口文件。 -
編譯模塊
:從入口文件出發(fā)雀哨,調(diào)用所有配置的loader對模塊進行翻譯,再找出該模塊依賴的模塊私爷,遞歸以上步驟描述雾棺,直到所有入口依賴的文件經(jīng)過了處理。 -
完成模塊編譯
:在經(jīng)過步驟4使用Loader對所有模塊都進行翻譯過后衬浑,得到每個模塊被翻譯后的最終內(nèi)容以及其相互之間的依賴關(guān)系捌浩。 -
輸出資源
:根據(jù)入口及模塊之間的依賴關(guān)系組裝成一個個包含多模塊的chunk,再把每個chunk轉(zhuǎn)換成一個單獨的文件加入到輸出列表工秩,此步驟為可修改輸出內(nèi)容的最后機會尸饺。 -
輸出完成
:在確定好輸出內(nèi)容后进统,根據(jù)配置確定輸出的路徑和文件名,把文件內(nèi)容寫入到文件系統(tǒng)侵佃。
在以上過程中麻昼,Webpack 會在特定的時間點廣播出特定的事件,插件在監(jiān)聽到感興趣的事件后會執(zhí)行特定的邏輯馋辈,并且插件可以調(diào)用 Webpack 提供的 API 改變 Webpack 的運行結(jié)果抚芦。
Loader和Plugin的區(qū)別及實現(xiàn)
提到 webpack,自然離不開 loader 與 blugin迈螟。Webpack 就像一條生產(chǎn)線叉抡,要經(jīng)過一系列處理流程后才能將源文件轉(zhuǎn)換成輸出結(jié)果(loader)。這條生產(chǎn)線上的每個處理流程的職責都是單一的答毫,多個流程之間有存在依賴關(guān)系褥民,只有完成當前處理后才能交給下一個流程去處理。而插件(plugin)就像是一個插入到生產(chǎn)線中的一個功能洗搂,在特定的時機對生產(chǎn)線上的資源做處理消返。
- Loader:用于對模塊源碼的轉(zhuǎn)換,loader描述了webpack如何處理非javascript模塊耘拇,并且在build中引入這些依賴撵颊。loader可以將文件從不同的語言(如TypeScript)轉(zhuǎn)換為JavaScript。
- Plugin:目的在于解決loader無法實現(xiàn)的其他事惫叛,從打包優(yōu)化和壓縮倡勇,到重新定義環(huán)境變量,功能強大到可以用來處理各種各樣的任務(wù)嘉涌。
簡而言之妻熊,loader可以理解成webpack的橫向廣度,有了loader仑最,webpack才可以打包處理各種的擴展語言扔役。而plugin可以理解為webpack的縱向深度,在生命周期內(nèi)注入不同的插件來擴展更多的能力词身。
Loader
Loader 就像是一個翻譯官厅目,每個 loader 可以把源資源轉(zhuǎn)換成新的結(jié)果輸出并傳遞給下一個 loader ,但是最后一個 Loader 必須返回 JavaScript (瀏覽器只能運行js代碼法严,不支持其他擴展語言)损敷。
以處理less文件為例:
module:{
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
- less-loader: 將 less 源代碼轉(zhuǎn)化為 css
- css-loader:處理 less-loader 輸出的 css,找出 css 中依賴的資源(@import 等)深啤,壓縮資源
- sytle-loader:處理 css-loader 輸出的 css拗馒,把 css 轉(zhuǎn)換成腳本加載的 js 代碼插入到 DOM 中
項目中常用的loader
-
file-loader
:把文件輸出到一個文件夾中,在代碼中通過相對 URL 去引用輸出的文件 (處理圖片和字體) -
url-loader
:與 file-loader 類似溯街,區(qū)別是用戶可以設(shè)置一個閾值诱桂,大于閾值會交給 file-loader 處理洋丐,小于閾值時返回文件 base64 形式編碼 (處理圖片和字體) -
image-loader
:加載并且壓縮圖片文件 -
svg-inline-loader
:將壓縮后的 SVG 內(nèi)容注入代碼中 -
babel-loader
:把 ES6 轉(zhuǎn)換成 ES5 -
ts-loader
: 將 TypeScript 轉(zhuǎn)換成 JavaScript -
awesome-typescript-loader
:將 TypeScript 轉(zhuǎn)換成 JavaScript,性能優(yōu)于 ts-loader -
sass-loader
:將SCSS/SASS代碼轉(zhuǎn)換成CSS -
less-loader
: 將 less 源代碼轉(zhuǎn)化為 css -
css-loader
:加載處理CSS挥等,支持模塊化友绝、壓縮、文件導(dǎo)入等特性 -
style-loader
:處理 css-loader 輸出的 css肝劲,把 css 轉(zhuǎn)換成腳本加載的 js 代碼插入到 DOM 中 -
eslint-loader
:通過 ESLint 檢查 JavaScript 代碼 -
tslint-loader
:通過 TSLint檢查 TypeScript 代碼 -
cache-loader
: 可以在一些性能開銷較大的 Loader 之前添加迁客,目的是將結(jié)果緩存到磁盤里
那么,實現(xiàn)一個loader
要求:把項目 txt 文件中的蔣梨花全部替換為梨花醬
1) 在config.js中配置項目中 .txt 結(jié)尾的文件使用我們的 demo-loader
// webpack.config.js
module:{
rules: [
{
test: /\.txt$/,
use: ['demo-loader'],
options: {
name: '梨花醬' // 將要變更的通過配置項傳入
}
}
]
}
2)創(chuàng)建一個包含蔣梨花的txt文件辞槐,并引用(webpack不會處理未引用的文件)
// test.txt
你好掷漱,我是蔣梨花
// app.js (入口文件引用)
const text = require(./text.txt)
console.log(test)
3)編寫 loader
// demo-loader.js
const loaderUtils = require('loader-utils')
// 接收options配置
module.exports = function(source) {
const options = loaderUtils.getOptions(this)
source = source.replace(/蔣梨花/g, options.name)
return `module.exports = ${JSON.stringify(sorce)}`
// 最終需要返回一段可執(zhí)行的js腳本
}
Plugin
plugin是運行在webpak打包過程中的某段邏輯,它主要的作用是根據(jù)webpack提供的一些hooks來進行一些額外的操作榄檬,使 webpack 更加靈活擴展卜范。
plugins: [
new HtmlWebpackPlugin()
]
我們通過 new 來使用這個插件,可以看出插件的本質(zhì)是一個構(gòu)造函數(shù)鹿榜。
首先了解兩個概念:Compiler 和Compilation
compiler 對象代表了完整的 webpack 環(huán)境配置海雪。這個對象在啟動 webpack 時被一次性建立,并配置好所有可操作的設(shè)置舱殿,包括 options喳魏,loader 和 plugin。當在 webpack 環(huán)境中應(yīng)用一個插件時怀薛,插件將收到此 compiler 對象的引用∶灾#可以使用它來訪問 webpack 的主環(huán)境枝恋。
compilation 對象代表了一次資源版本構(gòu)建。當運行 webpack 開發(fā)環(huán)境中間件時嗡害,每當檢測到一個文件變化焚碌,就會創(chuàng)建一個新的 compilation,從而生成一組新的編譯資源霸妹。一個 compilation 對象表現(xiàn)了當前的模塊資源十电、編譯生成資源、變化的文件叹螟、以及被跟蹤依賴的狀態(tài)信息鹃骂。compilation 對象也提供了很多關(guān)鍵時機的回調(diào),以供插件做自定義處理時選擇使用罢绽。
在 webpack 啟動后畏线,它會執(zhí)行 new xxxPlugin(options) 來初始化插件實例。在初始化對象后良价,會去調(diào)用 xxxPlugin.apply(compiler) 并傳入 compiler 對象寝殴。插件獲得 compiler 對象后蒿叠,可以通過
compiler.plugin('事件名', 回調(diào)函數(shù)) 的方式進行監(jiān)聽 webpack 廣播出來的事件了。
項目中常用的plugin
-
html-webpack-plugin
: 在打包結(jié)束后蚣常,?動生成?個 html ?文件抓歼,并把打包生成的js 模塊引?到該 html 中 -
clean-webpack-plugin
: 刪除(清理)構(gòu)建目錄 -
mini-css-extract-plugin
: 提取 CSS 到一個單獨的文件中 -
terser-webpack-plugin
: 支持壓縮 ES6 (Webpack4) -
webpack-merge
: 提取公共配置,減少重復(fù)配置代碼 -
HotModuleReplacementPlugin
: 模塊熱更新(啟用 HMR 很容易衣屏,且在大多數(shù)情況下不需要任何配置集币。)
那么,實現(xiàn)一個plugin
1) 在配置文件中泌射,使用插件
// webpack.config.js
plugins: [
new MyTestPlugin({
msg: '你好我是梨花醬' // 傳入的插件配置
})
]
2)編寫 plugin 插件
// MyTestPlugin.js
const { ConcatSource } = require("webpack-sources") // 用來寫入
class MyBannerPlugin {
constructor(options) {
// 獲取傳入的option信息
this.msg = options.msg
}, // 我們需要一個apply方法(為了獲取compiler)粘姜,接收compiler作為參數(shù)表示這次打包的上下文。
apply (compiler) {
const msg = this. msg // 指定掛載的 webpack 鉤子函數(shù)
// 使用compiler鉤子compilation熔酷,即編譯(compilation)創(chuàng)建之后孤紧,執(zhí)行插件。
compiler.hooks.compilation.tap("MyTestPlugin", compilation => {
// compilation的 optimizeChunkAssets 鉤子拒秘,可以利用這個鉤子實現(xiàn)為每個文件插入信息
compilation.hooks.optimizeChunkAssets.tap("MyTestPlugin", chunks => {
for (const chunk of chunks) {
for (const file of chunk.files) {
compilation.updateAsset(file, old => {
return new ConcatSource(msg,"\n", old);
});
}
}
})
})
}
}
module.exports = MyTestPlugin
可以看出号显,要實現(xiàn)一個plugin需要以下幾步:
- 首先需要聲明一個 class 構(gòu)造函數(shù)
- 在class里面定義一個apply方法,接收compiler作為參數(shù)表示這次打包的上下文躺酒。
- 指定掛載的webpack事件鉤子
- 處理webpack內(nèi)部實例的特定數(shù)據(jù)
- 功能完成后調(diào)用webpack提供的回調(diào)
具體的 compiler 鉤子 和 compilation 鉤子可以參考官方文檔:https://www.webpackjs.com/api/compilation-hooks/#optimizechunkassets
利用webpack進行優(yōu)化
-
JS壓縮:
webpack4.0
默認是使用 terser-webpack-plugin 這個壓縮插件押蚤,在此之前是使用 uglifyjs-webpack-plugin,兩者的區(qū)別是后者對 ES6 的壓縮不是很好羹应,同時我們可以開啟parallel
參數(shù)揽碘,使用多進程壓縮,加快壓縮园匹。 - CSS 壓縮:我們可以借助 optimize-css-assets-webpack-plugin 插件來壓縮 css雳刺,其默認使用的壓縮引擎是 cssnano
-
擦除無用的 CSS:
optimize-css-assets-webpack-plugin
+mini-css-extract-plugin
-
圖片壓縮: 我們就可以借助 image-webpack-loader 幫助我們來實現(xiàn)。(只要在
file-loader
之后加入image-webpack-loader
) -
減少圖片的HTTP請求:借助
url-loader
-
webpack-merge來整合兩個配置文件共同的配置
webpack.common.js
- 利用多線程提升構(gòu)建速度
thread-loader
-
預(yù)先編譯資源模塊
DllPlugin
-
緩存 Cache 相關(guān):
babel-loader
開啟緩存裸违、terser-webpack-plugin
開啟緩存掖桦、使用cache-loader
或者 hard-source-webpack-plugin
詳細內(nèi)容可參考:https://zhuanlan.zhihu.com/p/139498741