我的博客地址
文章目錄
- 項目及其技術(shù)棧介紹
- 前端: 項目初始化
- 前端: 使用Sass和Antd
- 前端: 開發(fā)體驗優(yōu)化
- 前端: 搭建路由和狀態(tài)管理
- 前端: 支持Axios
- 前端: 打包與環(huán)境變量設(shè)置
- 前端: 團隊代碼規(guī)范
- 后端: 項目初始化和使用Koa相關(guān)
- 后端: 使用TypeORM和MySQL
- 部署: 使用nginx部署前端項目
- 部署: 后端部署
- 部署: 使用jenkins自動化部署
前言
在前端開發(fā)完成后,都需要將編寫的代碼編譯打包成靜態(tài)文件,之后才會部署到服務(wù)器上,而WebPack就是這樣一個打包工具,它所有的loader、plugin都是圍繞這一個功能進行的擴展,這篇文章就在之前搭建的基礎(chǔ)上來介紹一下WebPack的打包配置吧,所用到的知識點如下:
- 打包路徑和命令添加
-
filename和chunkFilename:hash、chunkhash和contenthash publicPath- 單獨打包CSS
-
optimization優(yōu)化打包 - 環(huán)境變量設(shè)置區(qū)分生產(chǎn)開發(fā)環(huán)境
打包路徑和命令添加
-
添加打包路徑
其實在開始介紹前端搭建的第一篇文章中我們就已經(jīng)將打包的入口和出口都在webpack.config.js中制定好了:
image.png
這段配置意思是讓W(xué)ebPack通過src/index.tsx作為入口,遞歸地建立模塊依賴關(guān)系,然后打包輸出模塊,模塊輸出后存放在根目錄的dist文件夾中 -
添加打包命令
去到package.json加上如下命令:
image.png -
測試打包
我們運行npm run build之后可以看到包已經(jīng)打好了:
image.png
filename和chunkFilename
在webpack的output配置中,filename是打包文件名,也就是說我們列在entry中的文件打包出來的名字,在本例中就是app:

而
chunkFilename則是未列在entry中的模塊文件,下面劃紅線的都屬于chunk file:
而
filename和chunkFilename則是對這兩種文件命名的設(shè)定,這個命名很重要嗎?是的,因為瀏覽器存在緩存問題,如果按照目前配置,每一次代碼變化的情況下打包出來的文件名不變,瀏覽器優(yōu)先加載緩存中的文件,然后導(dǎo)致頁面沒有展示最新的頁面情況。
-
要解決這個問題我們先把
chunkFilename設(shè)置好:
image.png -
然后可以通過給文件名設(shè)置hash值的方式保證每次打包的文件名都是不同的,
filename和chunkFilename都支持該設(shè)置:
image.png -
另外這個hash值的設(shè)置有三種: 分別是
hash、chunkhash和contenthash-
hash:hash和整個項目的配置有關(guān),只要項目中有代碼改變,那么所有打包出來的hash值都會變,并且所有文件共用一個hash值 -
chunkhash:chunkhash和hash不同點在于,它根據(jù)入口文件進行依賴文件解析,然后構(gòu)建對應(yīng)的hash值,也就是每個打包出來的文件hash值都是不一樣的,每次修改代碼時候,他會根據(jù)依賴關(guān)系自動修改相關(guān)模塊的hash值,但是打包出來對應(yīng)的js和css文件的hash會相同。 -
contenthash: 在打包代碼的時候,一般會將CSS文件分離出來,然后我們通常會在組件中引入CSS文件,這時候如果使用的是chunkhash,在只修改組件js代碼的情況下因為對應(yīng)的css文件的hash值相同,打包出來的css文件的hash值也會跟著變,這時候就可以使用contenthash了,他會針對每個文件的內(nèi)容來計算hash值。
-
在本例中,我們就使用chunkhash即可,CSS的contenthash可以在后面對css文件進行分離的時候再配置。
publicPath
publicPath也是ouput中的一個屬性,用于處理打包后的引入資源路徑,比如上面我沒有添加publicPath的時候,打包出來的index.html引入資源的路徑:

現(xiàn)在我把
publicPath設(shè)置為/,再來看看打包出來的index.html的引入資源路徑就成了絕對路徑了:

那么這個配置有什么用處呢?
- 用處
在我們對前端項目打包完畢之后,js和css等較大的靜態(tài)資源都會被上傳到cdn上面,而index.html則會留在我們自己的服務(wù)器上,用戶訪問index.html的時候,index.html加載的css和js路徑就應(yīng)該是cdn域名上的資源,也就是說假設(shè)我的cdn域名為https://cdn.xxx.com/,那么這串東西就應(yīng)在放到publicPath中讓他自己在資源引入的前面加上,比如下面的例子:
image.png
image.png
單獨打包CSS
在前面的打包配置中,我們雖然可以打包成功,但是這時候的css是被嵌入到j(luò)s文件中的:

這對于模塊化來說不大好,而且還會導(dǎo)致js文件變得更大從而降低網(wǎng)頁加載速度,所以需要將css額外抽離出來。
使用
mini-css-extract-plugin抽離css
在webpack4.0后,推薦使用mini-css-extract-plugin來對css文件進行分離,首先我們需要安裝mini-css-extract-plugin包,這個包里面包含一個loader和一個plugin,后續(xù)需要分別進行配置
npm i -D mini-css-extract-plugin-
配置loader
接下來我們?nèi)サ?code>build/rules/styleRules.js文件中引入他:
image.png
在這里需要注意一點:
sass-loader是將sass文件編譯為css,而css-loader是將css轉(zhuǎn)為CommonJS模塊,style-loader是將對應(yīng)的JS生成為style 節(jié)點,那么如果我們要分離css文件的話,應(yīng)該是在css-loader之后進行,所以下面我們把之前style-loader的地方全都替換成MiniCssExtractPlugin:
image.png -
配置plugin
然后去到build/plugins.js文件中,引入并使用mini-css-extract-plugin并使用,配置主要是filename和chunkFilename:
image.png
注意這里需要用contenthash而不是chunkhash,否則打包出來的和引用該css的js文件的hash值是一樣的,而且改css還會導(dǎo)致打包后的js文件的hash值產(chǎn)生變化。 -
打包結(jié)果
可以看到css文件已經(jīng)被抽離了出來,并且js和css文件的hash值是不一樣的:
image.png
使用optimization優(yōu)化打包
在上面的打包配置中,因為之前做了路由組件的代碼分割,所以組件PageA和PageB會被單獨打包成js文件,再來看看打包的情況表,會發(fā)現(xiàn)page-a的公用模塊(引入的庫)體積高達800多k,這對于網(wǎng)頁加載資源而言是非常大的:

我們可以通過配置WebPack的
optimization來對代碼進一步分割,使得打出來的包更小。
-
首先我們?nèi)サ?code>build文件夾下新建文件
optimization.js,新建并導(dǎo)出一個對象:
image.png
然后配置runtimeChunk:
image.png
runtime是指webpack運行環(huán)境(模塊解析和加載)和模塊信息清單,而runtimeChunk就是詢問該清部分清單代碼是否單獨打包出來,我們這里將其單獨打包出來并命名為manifest。 -
然后我們通過配置
splitChunk的cacheGroups將一些公共模塊分離出來打包:
image.png
這里面的priority是優(yōu)先級的意思,數(shù)字越大優(yōu)先級越高,antd依賴moment,所以moment優(yōu)先級比antd高,然后commons是剩余模塊統(tǒng)一打包不做分割了。 -
接下來我們把optimization導(dǎo)入到webpack中去使用:
image.png
然后查看一下打包結(jié)果:
image.png
可以清楚的看到之前vendor~page-a文件從發(fā)800多k變成了500多k,另外還多出來一個vendor文件有270多k,說明是可以顯著減少打包出來的文件的大小的。
如果還需要繼續(xù)細分,可以繼續(xù)在splitChunks.cacheGroups配置中將公用模塊繼續(xù)細分并添加進去,使得打包的代碼越來越小 -
壓縮js優(yōu)化和壓縮css代碼并優(yōu)化
-
使用
terser-webpack-plugin優(yōu)化js壓縮過程:
terser-webpack-plugin是一個js代碼優(yōu)化插件,他可以使用多線程和緩存更快的壓縮js代碼,優(yōu)化打包體驗,并且使用非常簡單。
為什么使用這個插件呢?因為webpack默認使用的webpack.optimize.UglifyJsPlugin插件是不支持es6語法的,當(dāng)然你可以先用babel轉(zhuǎn)一下再用UglifyJsPlugin也沒所謂。
首先我們安裝npm i -D terser-webpack-plugin。
然后在build/optimization.js文件中配置minimizer,導(dǎo)入terser-webpack-plugin并使用即可:
image.png -
壓縮css代碼并優(yōu)化壓縮過程
在之前的打包中,我們的js代碼雖然已經(jīng)經(jīng)過了壓縮,但是css代碼還沒有壓縮:
image.png
這時候我們需要使用optimize-css-assets-webpack-plugin來做這件事。
首先安裝它npm i -D optimize-css-assets-webpack-plugin。
然后繼續(xù)在minimizer中進行配置:
image.png
-
最后我們重新打包來看看結(jié)果:

可以看到css文件也被壓縮了。
環(huán)境變量設(shè)置區(qū)分生產(chǎn)開發(fā)環(huán)境
在webpack4.0之后,打包的時候會出現(xiàn)這個警告:

這是因為在4.0后webpack受到parcel的競爭,而parcel就是號稱0配置的打包器,所以webpack也內(nèi)置了一套默認的打包配置,但是開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置是不一樣的,所以需要通過配置
webpack.mode屬性來告訴webpack處于什么環(huán)境,另外開發(fā)環(huán)境和生產(chǎn)環(huán)境有些不一樣,例如生產(chǎn)環(huán)境一般不需要sourcemap功能,之前打包出來很多.map文件就是因為開啟了sourcemap所致,所以我們也需要通過環(huán)境變量來對此進行區(qū)分。
- 使用
cross-env設(shè)置環(huán)境變量
cross-env是一個專門用于設(shè)置webpack運行或者打包時候的進程環(huán)境變量的工具,注意是進程環(huán)境變量而不是全局變量,這中變量在webpack打包結(jié)束后就沒有了,所以不能寫入到業(yè)務(wù)代碼中(當(dāng)然也是可以寫入的,不過需要另外配置)。
我們首先安裝它npm i -D cross-env。
然后去到package.json文件中,在script定義的命令中插入環(huán)境變量, 使用windows的同學(xué)需要注意,寫法可能有點不一樣,具體谷歌即可:

這一步的目的就是將
NODE_ENV這個變量插入到進程中,開發(fā)和生產(chǎn)分別是development和production。
然后我們?nèi)サ?code>webpack.config.js中,添加mode屬性的設(shè)置,通過process.env.NODE_ENV即可獲取之前設(shè)置的環(huán)境變量:

這樣就消除了上面所說的webpack的警告了。
-
通過環(huán)境變量分別配置開發(fā)生產(chǎn)webpack配置
既然已經(jīng)可以區(qū)分生產(chǎn)和開發(fā)環(huán)境,那么webpack中的有些配置也可以進行區(qū)分了:
image.png -
注入環(huán)境變量到代碼中
按照前面的說法,通過cross-env注入的變量只能存在于webpack打包或者編譯的進程中,那么有時候我們需要在代碼運行的過程中獲取到這個環(huán)境變量怎么辦呢?
例如上一章我們配置Axios的時候的baseURL就需要區(qū)分生產(chǎn)和開發(fā)環(huán)境:
image.png
當(dāng)然也是有解決方案的,我們可以使用WebPack自帶的插件DefinePlugin做到這點。
首先我們?nèi)サ?code>build/plugins.js文件中,在里面引入這個插件,然后進行變量配置即可:

之后我們到PageA組件中,使用process.env.NODE_ENV變量查看一下效果:

可以看到變量已經(jīng)被注入到了代碼中,而不是只存在于webpack打包和編譯的進程中了:

另外還需要記得把baseURL換成生產(chǎn)環(huán)境的url,在本例中,生產(chǎn)環(huán)境的請求url是https://test.oxcblog.club/api:

最后
經(jīng)過上面的這么多步驟,我們的webpack配置也基本算是告一段落了,下一篇文章將會介紹使用團隊代碼規(guī)范相關(guān)的插件: commitlint、eslint、stylelint等。




















