# 什么是 webpack
webpack
是個(gè)靜態(tài)模塊打包工具。在 webpack
看來,項(xiàng)目里所有資源皆模塊,利用資源依賴關(guān)系萍肆,把各模塊之間關(guān)聯(lián)起來袍榆。
簡(jiǎn)單講就: webpack
對(duì)有依賴關(guān)系的多個(gè)模塊文件進(jìn)行打包處理后,生成瀏覽器可以直接 高效運(yùn)行的資源塘揣。
通過 入口文件
開始包雀,利用 遞歸
找到直接依賴或間接依賴的所有模塊,并在內(nèi)部構(gòu)建一個(gè)能映射出項(xiàng)目所需的所有模塊的 依賴圖
勿负,并進(jìn)行 webpack
打包生成一個(gè)或多個(gè) bundle
文件馏艾。
打包是用的 webpack
命令即:npm webpack
從 webpack 4.x
開始,把 webpack
拆成 webpack
和 webpack-cli
兩部分奴愉,分工如下:
webpack:
負(fù)責(zé) Js 的打包工作
webpack-cli:
解析 webpack
命令,命令內(nèi)部使用 webpack
的功能
并且 webpack
能解析打包各種模塊規(guī)范的 Js
代碼铁孵,包括:ES6锭硼、Commonjs、AND/Requirejs 以及 CMD/Seajs
蜕劝。
優(yōu)點(diǎn)
- 專注于處理模塊化的項(xiàng)目檀头,能做到開箱即用,一步到位
- 可通過
plugin
擴(kuò)展岖沛,完整好用又不失靈活 - 使用場(chǎng)景不局限于
web
開發(fā) - 社區(qū)龐大活躍暑始,經(jīng)常引入緊跟時(shí)代發(fā)展的新特性,能為大多數(shù)場(chǎng)景找到已有的開源擴(kuò)展
- 良好的開發(fā)體驗(yàn)
缺點(diǎn)
只能用于采用模塊化開發(fā)的項(xiàng)目
# 什么是 loader ? 什么是 plugin ?
loader:模塊轉(zhuǎn)換器婴削,webpack
將一切文件視為模塊廊镜,但 webpack
只能解析 JavaScript
文件,而 loader 作用是讓 webpack
擁有了加載 和 解析非 JavaScript
文件的能力唉俗。
plugin:在 webpack
構(gòu)建流程中的特定時(shí)機(jī)注入擴(kuò)展邏輯嗤朴,讓它具有更多的靈活性。在 webpack
運(yùn)行的生命周期中會(huì)廣播出許多事件虫溜,plugin
可以監(jiān)聽這些事件雹姊,在合適的時(shí)機(jī)通過 webpack
提供的 API 改變輸出結(jié)果。
用法的區(qū)別:
Loader
在 module.rules
中配置衡楞,也就是說他作為模塊的解析規(guī)則而存在吱雏。 類型為數(shù)組,每一項(xiàng)都是一個(gè)Object瘾境,里面描述了對(duì)于什么類型的文件(test)歧杏,使用什么加載(loader)和使用的參數(shù)(options)
Plugin
在 plugins
中單獨(dú)配置。 類型為數(shù)組寄雀,每一項(xiàng)是一個(gè) plugin
的實(shí)例得滤,參數(shù)都通過構(gòu)造函數(shù)傳入。
# 有哪些常見的 Loader 盒犹?他們是解決什么問題的懂更?
file-loader
:把文件輸出到一個(gè)文件夾中眨业,在代碼中通過相對(duì) URL 去引用輸出的文件
url-loader
:和 file-loader 類似,但是能在文件很小的情況下以 base64 的方式把文件內(nèi)容注入到代碼中去
source-map-loader
:加載額外的 Source Map 文件沮协,以方便斷點(diǎn)調(diào)試
image-loader
:加載并且壓縮圖片文件
babel-loader
:把 ES6 轉(zhuǎn)換成 ES5
css-loader
:加載 CSS龄捡,支持模塊化、壓縮慷暂、文件導(dǎo)入等特性
style-loader
:把 CSS 代碼注入到 JavaScript 中聘殖,通過 DOM 操作去加載 CSS。
eslint-loader
:通過 ESLint 檢查 JavaScript 代碼
svg-inline-loader
:將壓縮后的 SVG 內(nèi)容注入代碼中
json-loader
: 加載 JSON 文件(默認(rèn)包含)
ts-loader
: 將 TypeScript 轉(zhuǎn)換成 JavaScript
awesome-typescript-loader
:將 TypeScript 轉(zhuǎn)換成 JavaScript行瑞,性能優(yōu)于 ts-loader
sass-loader
:將 CSS 代碼注入 JavaScript 中奸腺,通過 DOM 操作去加載 CSS
postcss-loader
:擴(kuò)展 CSS 語法,使用下一代 CSS血久,可以配合 autoprefixer 插件自動(dòng)補(bǔ)齊 CSS3 前綴
tslint-loader
:通過 TSLint檢查 TypeScript 代碼
vue-loader
:加載 Vue.js 單文件組件
# 有哪些常見的 Plugin突照?他們是解決什么問題的?
define-plugin
:定義環(huán)境變量
commons-chunk-plugin
:提取公共代碼
terser-webpack-plugin
: 支持壓縮 ES6 (Webpack4)
ignore-plugin
:忽略部分文件
html-webpack-plugin
:簡(jiǎn)化 HTML 文件創(chuàng)建 (依賴于 html-loader)
web-webpack-plugin
:可方便地為單頁應(yīng)用輸出 HTML氧吐,比 html-webpack-plugin 好用
mini-css-extract-plugin
: 分離樣式文件讹蘑,CSS 提取為獨(dú)立文件,支持按需加載
serviceworker-webpack-plugin
:為網(wǎng)頁應(yīng)用增加離線緩存功能
clean-webpack-plugin
: 刪除打包文件
happypack
:實(shí)現(xiàn)多線程加速編譯
# 如何利用 webpack 來優(yōu)化前端性能筑舅?
用 webpack
優(yōu)化前端性能是指優(yōu)化 webpack
的輸出結(jié)果座慰,讓打包的最終結(jié)果在瀏覽器運(yùn)行快速高效。
1.壓縮代碼翠拣。刪除多余的代碼版仔、注釋、簡(jiǎn)化代碼的寫法等等方式心剥。
用 UglifyJsPlugin
和ParallelUglifyPlugin
壓縮JS文件
用 mini-css-extract-plugin
壓縮 CSS
- 利用 CDN 加速邦尊。在構(gòu)建過程中,將引用的靜態(tài)資源路徑修改為CDN上對(duì)應(yīng)的路徑优烧〔踝幔可以利用
webpack
對(duì)于output
參數(shù)和各loader
的publicPath
參數(shù)來修改資源路徑 - 刪除死代碼。JS 用
Tree Shaking
畦娄,CSS 需要使用Purify-CSS
- 提取公共代碼又沾。用
CommonsChunkPlugin
插件
# 分別介紹 bundle,chunk熙卡,module 是什么
bundle
:是由 webpack
打包出來的文件杖刷,
chunk
:代碼塊,一個(gè) chunk
由多個(gè)模塊組合而成驳癌,用于代碼的合并和分割滑燃。
module
:是開發(fā)中的單個(gè)模塊,在 webpack
的世界颓鲜,一切皆模塊表窘,一個(gè)模塊對(duì)應(yīng)一個(gè)文件典予,webpack
會(huì)從配置的 entry
中遞歸開始找出所有依賴的模塊。
.說一下 webpack 的熱更新原理吧
熱更新又稱熱替換(Hot Module Replacement)乐严,縮寫為 HMR
瘤袖,基于 webpack-dev-server
。
當(dāng)你對(duì)代碼修改并保存后昂验,將會(huì)對(duì)代碼進(jìn)行重新打包捂敌,并將改動(dòng)的模塊發(fā)送到瀏覽器端,瀏覽器用新的模塊替換掉舊的模塊既琴,去實(shí)現(xiàn)局部更新頁面而非整體刷新頁面占婉。
# webpack 的構(gòu)建流程是什么?
webpack
的運(yùn)行流程是一個(gè)串行的過程,從啟動(dòng)到結(jié)束會(huì)依次執(zhí)行以下流程:
- 初始化參數(shù):從配置文件 和
Shell
語句中讀取與合并參數(shù)甫恩,得出最終的參數(shù)锐涯; - 開始編譯:用上一步得到的參數(shù)初始化 Compiler 對(duì)象,加載所有配置的插件填物,執(zhí)行對(duì)象的
run
方法開始執(zhí)行編譯; - 確定入口:根據(jù)配置中的
entry
找出所有的入口文件霎终; - 編譯模塊:從入口文件出發(fā)滞磺,調(diào)用所有配置的
Loader
對(duì)模塊進(jìn)行翻譯,再找出該模塊依賴的模塊莱褒,再遞歸本步驟直到所有入口依賴的文件都經(jīng)過了本步驟的處理击困;
完成模塊編譯:在經(jīng)過第4步使用Loader
翻譯完所有模塊后,得到了每個(gè)模塊被翻譯后的最終內(nèi)容以及它們之間的依賴關(guān)系广凸; - 輸出資源:根據(jù)入口和模塊之間的依賴關(guān)系阅茶,組裝成一個(gè)個(gè)包含多個(gè)模塊的
Chunk
,再把每個(gè)Chunk
轉(zhuǎn)換成一個(gè)單獨(dú)的文件加入到輸出列表谅海,這步是可以修改輸出內(nèi)容的最后機(jī)會(huì)脸哀; - 輸出完成:在確定好輸出內(nèi)容后,根據(jù)配置確定輸出的路徑和文件名扭吁,把文件內(nèi)容寫入到文件系統(tǒng)撞蜂。
在以上過程中,webpack
會(huì)在特定的時(shí)間點(diǎn)廣播出特定的事件侥袜,插件在監(jiān)聽到感興趣的事件后會(huì)執(zhí)行特定的邏輯蝌诡,并且插件可以調(diào)用webpack
提供的API
改變webpack
的運(yùn)行結(jié)果。
# 如何提高 webpack 的構(gòu)建速度枫吧?
- 多入口情況下浦旱,使用
CommonsChunkPlugin
來提取公共代碼 - 通過externals配置來提取常用庫
- 利用
DllPlugin
和DllReferencePlugin
預(yù)編譯資源模塊 通過DllPlugin
來對(duì)那些我們引用但是絕對(duì)不會(huì)修改的npm包來進(jìn)行預(yù)編譯,再通過DllReferencePlugin
將預(yù)編譯的模塊加載進(jìn)來九杂。 - 使用
Happypack
實(shí)現(xiàn)多線程加速編譯 - 使用
webpack-uglify-parallel
來提升uglifyPlugin
的壓縮速度颁湖。 原理上webpack-uglify-parallel
采用了多核并行壓縮來提升壓縮速度 - 使用
Tree-shaking
和Scope Hoisting
來剔除多余代碼
# webpack 文件之間相互依賴如何處理宣蠕?
.npm 打包時(shí)需要注意哪些?如何利用 webpack 來更好的構(gòu)建爷狈?
npm 是目前最大的 JavaScript 模塊倉庫植影,里面有來自全世界開發(fā)者上傳的可復(fù)用模塊。你可能只是 JS 模塊的使用者涎永,但是有些情況你也會(huì)去選擇上傳自己開發(fā)的模塊思币。 關(guān)于 NPM 模塊上傳的方法可以去官網(wǎng)上進(jìn)行學(xué)習(xí),這里只講解如何利用webpack來構(gòu)建羡微。
NPM模塊需要注意以下問題:
要支持 CommonJS 模塊化規(guī)范谷饿,所以要求打包后的最后結(jié)果也遵守該規(guī)則。
Npm模塊使用者的環(huán)境是不確定的妈倔,很有可能并不支持ES6博投,所以打包的最后結(jié)果應(yīng)該是采用ES5編寫的。并且如果ES5是經(jīng)過轉(zhuǎn)換的盯蝴,請(qǐng)最好連同SourceMap一同上傳毅哗。
Npm包大小應(yīng)該是盡量小(有些倉庫會(huì)限制包大信跬Α)
發(fā)布的模塊不能將依賴的模塊也一同打包虑绵,應(yīng)該讓用戶選擇性的去自行安裝。這樣可以避免模塊應(yīng)用者再次打包時(shí)出現(xiàn)底層模塊被重復(fù)打包的情況闽烙。
UI組件類的模塊應(yīng)該將依賴的其它資源文件翅睛,例如.css文件也需要包含在發(fā)布的模塊里。
基于以上需要注意的問題黑竞,我們可以對(duì)于webpack配置做以下擴(kuò)展和優(yōu)化:
CommonJS模塊化規(guī)范的解決方案: 設(shè)置output.libraryTarget='commonjs2'使輸出的代碼符合CommonJS2 模塊化規(guī)范捕发,以供給其它模塊導(dǎo)入使用
輸出ES5代碼的解決方案:使用babel-loader把 ES6 代碼轉(zhuǎn)換成 ES5 的代碼。再通過開啟devtool: 'source-map'輸出SourceMap以發(fā)布調(diào)試很魂。
Npm包大小盡量小的解決方案:Babel 在把 ES6 代碼轉(zhuǎn)換成 ES5 代碼時(shí)會(huì)注入一些輔助函數(shù)扎酷,最終導(dǎo)致每個(gè)輸出的文件中都包含這段輔助函數(shù)的代碼,造成了代碼的冗余莫换。解決方法是修改.babelrc文件霞玄,為其加入transform-runtime插件
不能將依賴模塊打包到NPM模塊中的解決方案:使用externals配置項(xiàng)來告訴webpack哪些模塊不需要打包。
對(duì)于依賴的資源文件打包的解決方案:通過css-loader和extract-text-webpack-plugin來實(shí)現(xiàn)
.文件指紋是什么拉岁?怎么用坷剧?
文件指紋是打包后輸出的文件名的后綴。
Hash:和整個(gè)項(xiàng)目的構(gòu)建相關(guān)喊暖,只要項(xiàng)目文件有修改惫企,整個(gè)項(xiàng)目構(gòu)建的 hash 值就會(huì)更改
Chunkhash:和 Webpack 打包的 chunk 有關(guān),不同的 entry 會(huì)生出不同的 chunkhash
Contenthash:根據(jù)文件內(nèi)容來定義 hash,文件內(nèi)容不變狞尔,則 contenthash 不變
JS的文件指紋設(shè)置
設(shè)置 output 的 filename丛版,用 chunkhash。
module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
}
}
CSS的文件指紋設(shè)置
設(shè)置 MiniCssExtractPlugin 的 filename偏序,使用 contenthash页畦。
module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
},
plugins:[
new MiniCssExtractPlugin({
filename: `[name][contenthash:8].css`
})
]
}
圖片的文件指紋設(shè)置
設(shè)置file-loader的name,使用hash研儒。
占位符名稱及含義
ext 資源后綴名
name 文件名稱
path 文件的相對(duì)路徑
folder 文件所在的文件夾
contenthash 文件的內(nèi)容hash豫缨,默認(rèn)是md5生成
hash 文件內(nèi)容的hash,默認(rèn)是md5生成
emoji 一個(gè)隨機(jī)的指代文件內(nèi)容的emoji
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename:'bundle.js',
path:path.resolve(__dirname, 'dist')
},
module:{
rules:[{
test:/\.(png|svg|jpg|gif)$/,
use:[{
loader:'file-loader',
options:{
name:'img/[name][hash:8].[ext]'
}
}]
}]
}
}
.在實(shí)際工程中端朵,配置文件上百行乃是常事好芭,如何保證各個(gè)loader按照預(yù)想方式工作?
可以使用 enforce 強(qiáng)制執(zhí)行 loader 的作用順序冲呢,pre 代表在所有正常 loader 之前執(zhí)行舍败,post 是所有 loader 之后執(zhí)行。(inline 官方不推薦使用)