我的博客地址
文章目錄
- 項(xiàng)目及其技術(shù)棧介紹
- 前端: 項(xiàng)目初始化
- 前端: 使用Sass和Antd
- 前端: 開發(fā)體驗(yàn)優(yōu)化
- 前端: 搭建路由和狀態(tài)管理
- 前端: 支持Axios
- 前端: 打包與環(huán)境變量設(shè)置
- 前端: 團(tuán)隊(duì)代碼規(guī)范
- 后端: 項(xiàng)目初始化和使用Koa相關(guān)
- 后端: 使用TypeORM和MySQL
- 部署: 使用nginx部署前端項(xiàng)目
- 部署: 后端部署
- 部署: 使用jenkins自動(dòng)化部署
前言
在前端開發(fā)完成后,都需要將編寫的代碼編譯打包成靜態(tài)文件,之后才會(huì)部署到服務(wù)器上,而WebPack就是這樣一個(gè)打包工具,它所有的loader、plugin都是圍繞這一個(gè)功能進(jìn)行的擴(kuò)展,這篇文章就在之前搭建的基礎(chǔ)上來(lái)介紹一下WebPack的打包配置吧,所用到的知識(shí)點(diǎn)如下:
- 打包路徑和命令添加
-
filename
和chunkFilename
:hash
、chunkhash
和contenthash
publicPath
- 單獨(dú)打包CSS
-
optimization
優(yōu)化打包 - 環(huán)境變量設(shè)置區(qū)分生產(chǎn)開發(fā)環(huán)境
打包路徑和命令添加
-
添加打包路徑
其實(shí)在開始介紹前端搭建的第一篇文章中我們就已經(jīng)將打包的入口和出口都在webpack.config.js
中制定好了:
image.png
這段配置意思是讓W(xué)ebPack通過src/index.tsx
作為入口,遞歸地建立模塊依賴關(guān)系,然后打包輸出模塊,模塊輸出后存放在根目錄的dist
文件夾中 -
添加打包命令
去到package.json
加上如下命令:
image.png -
測(cè)試打包
我們運(yùn)行npm run build
之后可以看到包已經(jīng)打好了:
image.png
filename和chunkFilename
在webpack
的output
配置中,filename
是打包文件名,也就是說(shuō)我們列在entry
中的文件打包出來(lái)的名字,在本例中就是app
:
而
chunkFilename
則是未列在entry
中的模塊文件,下面劃紅線的都屬于chunk file
:而
filename
和chunkFilename
則是對(duì)這兩種文件命名的設(shè)定,這個(gè)命名很重要嗎?是的,因?yàn)闉g覽器存在緩存問題,如果按照目前配置,每一次代碼變化的情況下打包出來(lái)的文件名不變,瀏覽器優(yōu)先加載緩存中的文件,然后導(dǎo)致頁(yè)面沒有展示最新的頁(yè)面情況。
-
要解決這個(gè)問題我們先把
chunkFilename
設(shè)置好:
image.png -
然后可以通過給文件名設(shè)置hash值的方式保證每次打包的文件名都是不同的,
filename
和chunkFilename
都支持該設(shè)置:
image.png -
另外這個(gè)hash值的設(shè)置有三種: 分別是
hash
、chunkhash
和contenthash
-
hash
:hash
和整個(gè)項(xiàng)目的配置有關(guān),只要項(xiàng)目中有代碼改變,那么所有打包出來(lái)的hash值都會(huì)變,并且所有文件共用一個(gè)hash值 -
chunkhash
:chunkhash
和hash
不同點(diǎn)在于,它根據(jù)入口文件進(jìn)行依賴文件解析,然后構(gòu)建對(duì)應(yīng)的hash值,也就是每個(gè)打包出來(lái)的文件hash值都是不一樣的,每次修改代碼時(shí)候,他會(huì)根據(jù)依賴關(guān)系自動(dòng)修改相關(guān)模塊的hash值,但是打包出來(lái)對(duì)應(yīng)的js和css文件的hash會(huì)相同。 -
contenthash
: 在打包代碼的時(shí)候,一般會(huì)將CSS文件分離出來(lái),然后我們通常會(huì)在組件中引入CSS文件,這時(shí)候如果使用的是chunkhash
,在只修改組件js代碼的情況下因?yàn)閷?duì)應(yīng)的css文件的hash值相同,打包出來(lái)的css文件的hash值也會(huì)跟著變,這時(shí)候就可以使用contenthash
了,他會(huì)針對(duì)每個(gè)文件的內(nèi)容來(lái)計(jì)算hash值。
-
在本例中,我們就使用chunkhash
即可,CSS的contenthash
可以在后面對(duì)css文件進(jìn)行分離的時(shí)候再配置。
publicPath
publicPath
也是ouput
中的一個(gè)屬性,用于處理打包后的引入資源路徑,比如上面我沒有添加publicPath
的時(shí)候,打包出來(lái)的index.html
引入資源的路徑:
現(xiàn)在我把
publicPath
設(shè)置為/
,再來(lái)看看打包出來(lái)的index.html
的引入資源路徑就成了絕對(duì)路徑了:那么這個(gè)配置有什么用處呢?
- 用處
在我們對(duì)前端項(xiàng)目打包完畢之后,js和css等較大的靜態(tài)資源都會(huì)被上傳到cdn上面,而index.html
則會(huì)留在我們自己的服務(wù)器上,用戶訪問index.html
的時(shí)候,index.html
加載的css和js路徑就應(yīng)該是cdn域名上的資源,也就是說(shuō)假設(shè)我的cdn域名為https://cdn.xxx.com/
,那么這串東西就應(yīng)在放到publicPath
中讓他自己在資源引入的前面加上,比如下面的例子:
image.png
image.png
單獨(dú)打包CSS
在前面的打包配置中,我們雖然可以打包成功,但是這時(shí)候的css是被嵌入到j(luò)s文件中的:
這對(duì)于模塊化來(lái)說(shuō)不大好,而且還會(huì)導(dǎo)致js文件變得更大從而降低網(wǎng)頁(yè)加載速度,所以需要將css額外抽離出來(lái)。
使用
mini-css-extract-plugin
抽離css
在webpack4.0后,推薦使用mini-css-extract-plugin
來(lái)對(duì)css文件進(jìn)行分離,首先我們需要安裝mini-css-extract-plugin
包,這個(gè)包里面包含一個(gè)loader和一個(gè)plugin,后續(xù)需要分別進(jìn)行配置
npm i -D mini-css-extract-plugin
-
配置loader
接下來(lái)我們?nèi)サ?code>build/rules/styleRules.js文件中引入他:
image.png
在這里需要注意一點(diǎn):
sass-loader
是將sass文件編譯為css,而css-loader
是將css轉(zhuǎn)為CommonJS模塊,style-loader
是將對(duì)應(yīng)的JS生成為style 節(jié)點(diǎn),那么如果我們要分離css文件的話,應(yīng)該是在css-loader
之后進(jìn)行,所以下面我們把之前style-loader
的地方全都替換成MiniCssExtractPlugin
:
image.png -
配置plugin
然后去到build/plugins.js
文件中,引入并使用mini-css-extract-plugin
并使用,配置主要是filename
和chunkFilename
:
image.png
注意這里需要用contenthash
而不是chunkhash
,否則打包出來(lái)的和引用該css的js文件的hash值是一樣的,而且改css還會(huì)導(dǎo)致打包后的js文件的hash值產(chǎn)生變化。 -
打包結(jié)果
可以看到css文件已經(jīng)被抽離了出來(lái),并且js和css文件的hash值是不一樣的:
image.png
使用optimization優(yōu)化打包
在上面的打包配置中,因?yàn)橹白隽寺酚山M件的代碼分割,所以組件PageA
和PageB
會(huì)被單獨(dú)打包成js文件,再來(lái)看看打包的情況表,會(huì)發(fā)現(xiàn)page-a
的公用模塊(引入的庫(kù))體積高達(dá)800多k,這對(duì)于網(wǎng)頁(yè)加載資源而言是非常大的:
我們可以通過配置WebPack的
optimization
來(lái)對(duì)代碼進(jìn)一步分割,使得打出來(lái)的包更小。
-
首先我們?nèi)サ?code>build文件夾下新建文件
optimization.js
,新建并導(dǎo)出一個(gè)對(duì)象:
image.png
然后配置runtimeChunk
:
image.png
runtime是指webpack運(yùn)行環(huán)境(模塊解析和加載)和模塊信息清單,而runtimeChunk
就是詢問該清部分清單代碼是否單獨(dú)打包出來(lái),我們這里將其單獨(dú)打包出來(lái)并命名為manifest
。 -
然后我們通過配置
splitChunk
的cacheGroups
將一些公共模塊分離出來(lái)打包:
image.png
這里面的priority
是優(yōu)先級(jí)的意思,數(shù)字越大優(yōu)先級(jí)越高,antd依賴moment,所以moment優(yōu)先級(jí)比antd高,然后commons
是剩余模塊統(tǒng)一打包不做分割了。 -
接下來(lái)我們把optimization導(dǎo)入到webpack中去使用:
image.png
然后查看一下打包結(jié)果:
image.png
可以清楚的看到之前vendor~page-a
文件從發(fā)800多k變成了500多k,另外還多出來(lái)一個(gè)vendor
文件有270多k,說(shuō)明是可以顯著減少打包出來(lái)的文件的大小的。
如果還需要繼續(xù)細(xì)分,可以繼續(xù)在splitChunks.cacheGroups
配置中將公用模塊繼續(xù)細(xì)分并添加進(jìn)去,使得打包的代碼越來(lái)越小 -
壓縮js優(yōu)化和壓縮css代碼并優(yōu)化
-
使用
terser-webpack-plugin
優(yōu)化js壓縮過程:
terser-webpack-plugin
是一個(gè)js代碼優(yōu)化插件,他可以使用多線程和緩存更快的壓縮js代碼,優(yōu)化打包體驗(yàn),并且使用非常簡(jiǎn)單。
為什么使用這個(gè)插件呢?因?yàn)閣ebpack默認(rèn)使用的webpack.optimize.UglifyJsPlugin
插件是不支持es6語(yǔ)法的,當(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
這時(shí)候我們需要使用optimize-css-assets-webpack-plugin
來(lái)做這件事。
首先安裝它npm i -D optimize-css-assets-webpack-plugin
。
然后繼續(xù)在minimizer
中進(jìn)行配置:
image.png
-
最后我們重新打包來(lái)看看結(jié)果:
可以看到css文件也被壓縮了。
環(huán)境變量設(shè)置區(qū)分生產(chǎn)開發(fā)環(huán)境
在webpack4.0之后,打包的時(shí)候會(huì)出現(xiàn)這個(gè)警告:
這是因?yàn)樵?.0后webpack受到parcel的競(jìng)爭(zhēng),而parcel就是號(hào)稱0配置的打包器,所以webpack也內(nèi)置了一套默認(rèn)的打包配置,但是開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置是不一樣的,所以需要通過配置
webpack.mode
屬性來(lái)告訴webpack處于什么環(huán)境,另外開發(fā)環(huán)境和生產(chǎn)環(huán)境有些不一樣,例如生產(chǎn)環(huán)境一般不需要sourcemap
功能,之前打包出來(lái)很多.map
文件就是因?yàn)殚_啟了sourcemap
所致,所以我們也需要通過環(huán)境變量來(lái)對(duì)此進(jìn)行區(qū)分。
- 使用
cross-env
設(shè)置環(huán)境變量
cross-env
是一個(gè)專門用于設(shè)置webpack運(yùn)行或者打包時(shí)候的進(jìn)程環(huán)境變量的工具,注意是進(jìn)程環(huán)境變量而不是全局變量,這中變量在webpack打包結(jié)束后就沒有了,所以不能寫入到業(yè)務(wù)代碼中(當(dāng)然也是可以寫入的,不過需要另外配置)。
我們首先安裝它npm i -D cross-env
。
然后去到package.json
文件中,在script
定義的命令中插入環(huán)境變量, 使用windows的同學(xué)需要注意,寫法可能有點(diǎn)不一樣,具體谷歌即可:
這一步的目的就是將
NODE_ENV
這個(gè)變量插入到進(jìn)程中,開發(fā)和生產(chǎn)分別是development
和production
。
然后我們?nèi)サ?code>webpack.config.js中,添加mode
屬性的設(shè)置,通過process.env.NODE_ENV
即可獲取之前設(shè)置的環(huán)境變量:
這樣就消除了上面所說(shuō)的webpack的警告了。
-
通過環(huán)境變量分別配置開發(fā)生產(chǎn)webpack配置
既然已經(jīng)可以區(qū)分生產(chǎn)和開發(fā)環(huán)境,那么webpack中的有些配置也可以進(jìn)行區(qū)分了:
image.png -
注入環(huán)境變量到代碼中
按照前面的說(shuō)法,通過cross-env
注入的變量只能存在于webpack打包或者編譯的進(jìn)程中,那么有時(shí)候我們需要在代碼運(yùn)行的過程中獲取到這個(gè)環(huán)境變量怎么辦呢?
例如上一章我們配置Axios的時(shí)候的baseURL
就需要區(qū)分生產(chǎn)和開發(fā)環(huán)境:
image.png
當(dāng)然也是有解決方案的,我們可以使用WebPack自帶的插件DefinePlugin
做到這點(diǎn)。
首先我們?nèi)サ?code>build/plugins.js文件中,在里面引入這個(gè)插件,然后進(jìn)行變量配置即可:
之后我們到PageA
組件中,使用process.env.NODE_ENV
變量查看一下效果:
可以看到變量已經(jīng)被注入到了代碼中,而不是只存在于webpack打包和編譯的進(jìn)程中了:
另外還需要記得把baseURL換成生產(chǎn)環(huán)境的url,在本例中,生產(chǎn)環(huán)境的請(qǐng)求url是https://test.oxcblog.club/api
:
最后
經(jīng)過上面的這么多步驟,我們的webpack配置也基本算是告一段落了,下一篇文章將會(huì)介紹使用團(tuán)隊(duì)代碼規(guī)范相關(guān)的插件: commitlint、eslint、stylelint等。