在以前,為了減少 HTTP 請求屠缭,通常地箍鼓,我們都會把所有的代碼都打包成一個單獨的 JS 文件。但是呵曹,如果這個 JS 文件體積很大的話款咖,那就得不償失了。
就拿咱們產(chǎn)品來說奄喂,都是 SPA 單頁面應(yīng)用铐殃,我們不可能在首屏加載所有的 JS 和 CSS 代碼。
這時跨新,我們不妨把所有代碼分成一塊一塊富腊,需要某塊代碼的時候再去加載它;還可以利用瀏覽器的緩存域帐,下次用到它的話赘被,直接從緩存中讀取。很顯然肖揣,這種做法可以加快我們網(wǎng)頁的加載速度民假,webpack 剛好可以幫助我們。
1许饿、什么是 webpack
- WebPack可以看做是模塊打包機:它做的事情是阳欲,分析你的項目結(jié)構(gòu),找到 JavaScript 模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript 等)球化,并將其打包為合適的格式以供瀏覽器使用秽晚。
2、為什么要使用 webpack
-
現(xiàn)在的很多網(wǎng)頁其實可以看做是功能豐富的應(yīng)用筒愚,它們擁有著復(fù)雜的 JavaScript 代碼和一大堆依賴包赴蝇。為了簡化開發(fā)的復(fù)雜度,前端社區(qū)涌現(xiàn)出了很多好的實踐方法
模塊化巢掺,讓我們可以把復(fù)雜的程序細化為小的文件
類似于 TypeScript 這種在 JavaScript 基礎(chǔ)上拓展的開發(fā)語言:使我們能夠?qū)崿F(xiàn)目前版本的 JavaScript 不能直接使用的特性句伶,并且之后還能轉(zhuǎn)換為 JavaScript 文件使瀏覽器可以識別
scss,less等CSS預(yù)處理器
這些改進確實大大的提高了我們的開發(fā)效率陆淀,但是利用它們開發(fā)的文件往往需要進行額外的處理才能讓瀏覽器識別, 而手動處理又是非常繁瑣的考余,這就為 WebPack 類的工具的出現(xiàn)提供了需求
3、webpack 入門
- 創(chuàng)建項目目錄如下:
webpack-demo
|- package.json
+ |- index.html
+ |- /src
+ |- index.js
安裝 webpack
npm install webpack webpack-cli --save-dev
執(zhí)行
npx webpack
4轧苫、配置 webpack.config.js
- 基礎(chǔ)配置 mode楚堤、entry、output
const path = require('path')
?
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'src/index'),
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'bundle')
}
}
5含懊、plugins
Plugin (插件) 是 webpack 生態(tài)的的一個關(guān)鍵部分身冬。它為社區(qū)提供了一種強大的方法來擴展 webpack 和開發(fā) webpack 的編譯過程
在特定的時刻,做特定的事情
在 webpack 運行的生命周期中會廣播出許多的事件岔乔,plugin 可以監(jiān)聽這些事件酥筝,在合適的時機通過 webpack 提供的 API 改變輸出的結(jié)果
clean-webpack-plugin、html-webpack-plugin 實例
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
?
const HtmlPlugin = new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'index.html')
})
?
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'src/index'),
output: {
filename: '[name].[chunkHash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
HtmlPlugin
]
}
6雏门、loader
loader 就是一個打包的方案嘿歌。對于特定的非 js 模塊告訴 webpack 該如何打包。
webpack 根據(jù)正則表達式剿配,來確定應(yīng)該查找哪些文件搅幅,并將其提供給指定的 loader。在這種情況下呼胚,以 .css 結(jié)尾的全部文件茄唐,都將被提供給 style-loader 和 css-loader。
loader 執(zhí)行順序:從上到下蝇更,從右到左
style-loader沪编、css-loader、less-loader 實例
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
?
const HtmlPlugin = new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'index.html')
})
?
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'src/index'),
output: {
filename: '[name].[chunkHash:8].js',
path: path.resolve(__dirname, 'dist')
},
module:{
rules: [{
test: /\.js$/,
use: ['babel-loader']
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, {
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}]
},
plugins: [
new CleanWebpackPlugin(),
HtmlPlugin
]
}
7年扩、webpack-dev-server
--inline 刷新頁面 --hot 熱更新
--progress 顯示進度條
--color 顏色
--open 打開瀏覽器
--port 8001 端口
// package.json "start": "webpack-dev-server --inline --progress --color --open --port 8001"
8蚁廓、 webpack 構(gòu)建流程
初始化參數(shù),從配置文件和 shell 語句中讀到的參數(shù)合并厨幻,得到最后的參數(shù)
開始編譯:用合并得到的參數(shù)初始化 complier 對象相嵌,加載所有配置的插件腿时,執(zhí)行 run 方法開始編譯
確定入口,通過 entry 找到入口文件
編譯模塊饭宾,從入口文件出發(fā)批糟,調(diào)用所有配置的 loader 對模塊進行解析翻譯,遞歸找到該模塊依賴的模塊進行處理
完成模塊編譯看铆,得到每個模塊被翻譯之后的最終的內(nèi)容和依賴關(guān)系
輸出資源徽鼎,根據(jù)入口和模塊之間的依賴關(guān)系,組裝成一個個包含多個模塊的 chunk弹惦,在把每個 chunk 轉(zhuǎn)換成一個單獨的文件加載到輸出列表(這一步是修改輸出內(nèi)容的最后機會)
輸出完成否淤,根據(jù)配置中輸出的路徑和文件名,把內(nèi)容寫到文件系統(tǒng)中
在以上過程中棠隐,webpack會在特定的時間點廣播出特定的事件石抡,插件在監(jiān)聽事件后會執(zhí)行特定的邏輯,改變 webpack 的運行結(jié)果
9宵荒、優(yōu)化 Webpack 的構(gòu)建
使用高版本的 Webpack 和 Node.js汁雷, 多進程/多實例構(gòu)建:HappyPack(不維護了)净嘀、thread-loader
壓縮代碼
webpack-parallel-uglify-plugin
uglifyjs-webpack-plugin 開啟 parallel 參數(shù) (不支持ES6)
terser-webpack-plugin 開啟 parallel 參數(shù)报咳,多進程并行壓縮
通過 mini-css-extract-plugin 提取 Chunk 中的 CSS 代碼到單獨文件,通過 css-loader 的 minimize 選項開啟 cssnano 壓縮 CSS挖藏。
使用基于 Node 庫的 imagemin (很多定制選項暑刃、可以處理多種圖片格式)
配置 image-webpack-loader 圖片壓縮
-
縮小打包作用域:
exclude/include (確定 loader 規(guī)則范圍)
resolve.modules 指明第三方模塊的絕對路徑 (減少不必要的查找)
resolve.mainFields 只采用 main 字段作為入口文件描述字段 (減少搜索步驟,需要考慮到所有運行時依賴的第三方模塊的入口文件描述字段)
resolve.extensions 盡可能減少后綴嘗試的可能性
noParse 對完全不需要解析的庫進行忽略 (不去解析但仍會打包到 bundle 中膜眠,注意被忽略掉的文件里不應(yīng)該包含 import岩臣、require、define 等模塊化語句)
IgnorePlugin (完全排除模塊)忽略本地化內(nèi)容之打包核心模塊
合理使用 alias
-
提取頁面公共資源:
使用 html-webpack-externals-plugin宵膨,將基礎(chǔ)包通過 CDN 引入架谎,不打入 bundle 中
使用 SplitChunksPlugin 進行(公共腳本、基礎(chǔ)包辟躏、頁面公共文件)分離(Webpack4內(nèi)置) 谷扣,替代了 CommonsChunkPlugin 插件
-
基礎(chǔ)包分離:
使用 DllPlugin 進行分包,使用 DllReferencePlugin(索引鏈接) 對 manifest.json 引用捎琐,讓一些基本不會改動的代碼先打包成靜態(tài)資源会涎,避免反復(fù)編譯浪費時間。
HashedModuleIdsPlugin 可以解決模塊數(shù)字 id 問題
-
充分利用緩存提升二次構(gòu)建速度:
babel-loader 開啟緩存
terser-webpack-plugin 開啟緩存
使用 cache-loader 或者 hard-source-webpack-plugin
-
Tree shaking
打包過程中檢測工程中沒有引用過的模塊并進行標記瑞凑,在資源壓縮時將它們從最終的bundle中去掉(只能對ES6 Modlue生效) 開發(fā)中盡可能使用ES6 Module的模塊末秃,提高 tree shaking 效率
purgecss-webpack-plugin 和 mini-css-extract-plugin配合使用(建議)
禁用 babel-loader 的模塊依賴解析,否則 Webpack 接收到的就都是轉(zhuǎn)換過的 CommonJS 形式的模塊籽御,無法進行 tree-shaking
使用 PurifyCSS(不在維護) 或者 uncss 去除無用 CSS 代碼
-
Scope Hoisting
構(gòu)建后的代碼會存在大量閉包练慕,造成體積增大惰匙,運行代碼時創(chuàng)建的函數(shù)作用域變多,內(nèi)存開銷變大铃将。Scope hoisting 將所有模塊的代碼按照引用順序放在一個函數(shù)作用域里徽曲,然后適當?shù)闹孛恍┳兞恳苑乐棺兞棵麤_突
必須是ES6的語法,因為有很多第三方庫仍采用 CommonJS 語法麸塞,為了充分發(fā)揮 Scope hoisting 的作用秃臣,需要配置 mainFields 對第三方模塊優(yōu)先采用 jsnext:main 中指向的ES6模塊化語法
webpack.optimize.ModuleConcatenationPlugin 開啟 Scope Hoisting 作用域提升,提升代碼在瀏覽器中的執(zhí)行速度
10哪工、vue-cli 腳手架簡析
npm install @vue/cli
vue create myApp