Hi ~ 許久沒(méi)更新的小簡(jiǎn)書(shū),自從換了新工作,日夜掉發(fā)式的瘋狂加班,一個(gè)半月的時(shí)間學(xué)到了不少東西,趁熱記錄一波吧~

一個(gè)月前,接到了一個(gè)需求,并且長(zhǎng)期需要維護(hù)多個(gè)無(wú)關(guān)的活動(dòng)頁(yè)或者場(chǎng)景頁(yè),自以為簡(jiǎn)單的我樂(lè)呵呵的接了需求,心中已經(jīng)想好“用react來(lái)搭一個(gè)多頁(yè)面應(yīng)用就夠符合需求啦~”,事實(shí)證明,我還是too young too simple ,sometimes naive...

實(shí)際開(kāi)發(fā)工作中,react多頁(yè)面應(yīng)用的腳手架GitHub上有很多,自己也可以根據(jù)官方的react腳手架就可以愉快的擼起來(lái),但是目前遇到的比較頭疼的問(wèn)題是,怎么讓我的react應(yīng)用代碼壓縮模塊打包,這個(gè)時(shí)候就很有必要好好研究下webpack的使用了。
此次分享主要是針對(duì)多頁(yè)面的webpack4打包。
So,咱們來(lái)聊一下,什么是webpack?

官網(wǎng)上的webpack相信所有前端開(kāi)發(fā)童鞋都見(jiàn)過(guò),webpack做的事情,就是將我們的js應(yīng)用的各個(gè)文件按照模塊打包成多個(gè)bundle,是一個(gè)靜態(tài)模塊的打包器。
webpack做的事情,就是分析項(xiàng)目結(jié)構(gòu),找到j(luò)s模塊以及一些不能直接運(yùn)行的語(yǔ)言,并將其打包成合適的格式給瀏覽器執(zhí)行。
安裝webpack
npm install webpack-cli
了解webpack前需要知道webpack的四大核心概念,entry、output、loader、plugins。
Entry(入口)
entry是webpack的起點(diǎn)指示,告訴webpack從哪里開(kāi)始入手打包任務(wù),用來(lái)指定入口,默認(rèn)值是./src。
如果是做單頁(yè)面程序,單個(gè)入口。
//單個(gè)入口的寫(xiě)法 entry: string|Array<string>
entry:{
main:'../src/component/my.js'
}
entry:'../src/component/my.js'
//如果傳數(shù)組形式,是注入多個(gè)入口以及多個(gè)依賴(lài)文件。
如果是做多個(gè)頁(yè)面,多個(gè)入口。
//多個(gè)入口的寫(xiě)法,所有的文件會(huì)被打包到dist文件中。
entry:{
app:['./a.js','./b.js','./c.js']
}
//最終輸出 dist/a.js / dist/b.js dist/c.js
當(dāng)webpack在解析代碼時(shí),每遇到import或者require引入的依賴(lài),最終會(huì)被打包在最終構(gòu)建結(jié)果中。打包的最終結(jié)果只會(huì)引入該模塊引入的依賴(lài)。
output(輸出)
打包完后輸出的位置
output: {
filename: 'vendor.js',
path: '/home/index'
}
loader(加載器)
實(shí)際上webpack只能打包js文件,其他資源例如css和html是需要加載器來(lái)將資源轉(zhuǎn)化,加載進(jìn)來(lái)。
舉個(gè)栗子,如果項(xiàng)目中使用了sass的樣式語(yǔ)法,是無(wú)法被瀏覽器所識(shí)別的,因此需要引用loader,將sass轉(zhuǎn)義成能被識(shí)別的css語(yǔ)法閱讀。
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
在該項(xiàng)目中,react用的是jsx的語(yǔ)法,同樣無(wú)法被瀏覽器所識(shí)別,需要引用babel將jsx語(yǔ)法轉(zhuǎn)義成正常的js運(yùn)行。
module:{
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",//可以根據(jù)配置的目標(biāo)瀏覽器或者運(yùn)行環(huán)境來(lái)自動(dòng)將2015+的代碼轉(zhuǎn)為es5
"@babel/preset-react",
{ "plugins": ["@babel/plugin-proposal-class-properties"] }//這句可以在項(xiàng)目中使用箭頭函數(shù)
],
}
},
]
}
loader的職責(zé)是單一的,一個(gè)loader相當(dāng)于一個(gè)翻譯官,只做翻譯某種語(yǔ)言,需要關(guān)心輸入和輸出。
一個(gè)項(xiàng)目里有多個(gè)翻譯官,在調(diào)用多個(gè) Loader 去轉(zhuǎn)換一個(gè)文件時(shí),每個(gè) Loader 會(huì)鏈?zhǔn)降捻樞驁?zhí)行, 第一個(gè) Loader 將會(huì)拿到需處理的原內(nèi)容,上一個(gè) Loader 處理后的結(jié)果會(huì)傳給下一個(gè)接著處理,最后的 Loader 將處理后的最終結(jié)果返回給 Webpack。
plugins(插件)
常用的插件有
- webpack-dev-server
- copy-webpack-plugin
- uglifyjs-webpack-plugin
- webpack-spritesmith
- clean-webpack-plugin
- html-webpack-plugin
...還有好多好多
webpack-dev-server
非常常見(jiàn),用于啟動(dòng)本地開(kāi)發(fā)模式,本身是一個(gè)express服務(wù)器,封裝了webpack-hot-middleware,實(shí)現(xiàn)了熱更新。
devServer:{
host:'0.0.0.0' || 'localhost',
post:9000,//打開(kāi)的端口
open:true,//自動(dòng)打開(kāi)
contentBase:path.join(_dirname,'src')//不設(shè)置的話(huà),默認(rèn)是當(dāng)前執(zhí)行的目錄,一般是項(xiàng)目根目錄,會(huì)在項(xiàng)目根目錄查找index.html文件。
}
copy-webpack-plugin
在webpack相中拷貝文件或文件夾的方法。
new CopyWebpackPlugin({
from:'.....',//需要拷貝的源目標(biāo)文件
to:'',//拷貝后存放的文件位置
})
uglifyjs-webpack-plugin
用來(lái)壓縮優(yōu)化js文件,webpack4+以上的版本可使用。
webpack4之前的版本是通過(guò)webpack.optimize.commonsChuckPlugin來(lái)壓縮js
module.exports={
optimization:{
minimizer:true,//默認(rèn)為true,效果就是壓縮js代碼
splitChuncks:{
chunks:'async',//'async':分割異步打包的代碼,'all':同時(shí)分割同步和異步代碼
cacheGroups:{//默認(rèn)的規(guī)則不會(huì)打包,需要單獨(dú)定義
vendors:{
test:/[\\/]node_modules[\\/]/,
name:'vendors'
},
commons:{
test:/commons\.js/,
name:'commons'
}
}
}
}
}
webpack-spritesmith
把零散的小圖生成一張雪碧圖,減少http請(qǐng)求
new SpritesmithPlugin({
// 目標(biāo)小圖標(biāo)
src: {
cwd: `src/assets/images/sprites`,
glob: '*.png'
},
// 輸出雪碧圖文件及樣式文件7
target: {
image: `dist/static/images/sprite-[hash].png`,
css: [
[
`dist/static/css/sprite.css`, {
formatOpts: {
cssSelector: (groupName) => `.icon-${groupName.name}` // 修改生成的sprite css中類(lèi)名定義
}
}
]
]
},
// 樣式文件中調(diào)用雪碧圖地址寫(xiě)法
apiOptions: {
cssImageRef: '../images/sprite-[hash].png'
},
spritesmithOptions: {
algorithm: 'top-down'
}
})
clean-webpack-plugin
用來(lái)清除文件
// 刪除文件 保留新文件
new CleanWebpackPlugin(['dist']),
html-webpack-plugin
為入口文件html加載js依賴(lài),動(dòng)態(tài)添加編譯后的hash值文件,防止引用緩存文件的問(wèn)題。
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({ // 打包輸出HTML
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'a.html',
template: 'a.html'
}),
new HtmlWebpackPlugin({ // 打包輸出HTML
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'b.html',
template: 'b.html'
}),
]
項(xiàng)目是多頁(yè)的情況下,傳入的html入口文件設(shè)置多個(gè)即可。
可以寫(xiě)一個(gè)提取html的函數(shù),批量獲取,組合成數(shù)組形式。
resolve配置
webpack在啟動(dòng)后會(huì)從配置的入口模塊出發(fā)去找尋所有依賴(lài)的模塊,resolve配置webpack如何找尋模塊所對(duì)應(yīng)的文件。
alias
resolve.alias配置項(xiàng)通過(guò)設(shè)置其他名字來(lái)將原路徑映射成新的路徑
resolve:{
extensions: ['.js', '.json','.css'],//導(dǎo)入的語(yǔ)句中如果沒(méi)帶文件后綴名,webpack會(huì)自動(dòng)帶上后綴名去嘗試文件是否存在,用于配置在嘗試過(guò)程中用到的后綴列表。
alias:{
'api':utils.resolve('/src/common/api/'),//通過(guò)api關(guān)鍵字替換'/src/common/api/'
}
}
總結(jié)
歸結(jié)webpack在實(shí)際項(xiàng)目中做了哪些事情?
1、webpack從context設(shè)置的文件位置開(kāi)始找尋
2、尋找entry里的所有文件名
3、在js文件編譯過(guò)程中,遇到 import | require 引入的依賴(lài)文件,然后在依賴(lài)文件遞歸找尋所有依賴(lài),最終打包在最終編譯生成的文件。
4、webpack把所有生成的文件都輸出到output.path路徑中,以output.filename對(duì)應(yīng)命名的模塊來(lái)命名。
webpack之所以強(qiáng)大,是在于插件各式各樣,總能滿(mǎn)足你的需求。webpack4的新api大大的提高了Code Splitting 的體驗(yàn),以上講的內(nèi)容還是淺顯了點(diǎn),具體的一些細(xì)節(jié)還是需要靠同志們自己愉快的擼起來(lái),只有實(shí)操才是檢驗(yàn)?zāi)芰Φ奈ㄒ煌緩絶

最后,po上項(xiàng)目中的webpack相關(guān)依賴(lài)的版本情況
{
"@babel/core": "^7.1.2",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-transform-runtime": "^7.3.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4",
"babel-loader": "^8.0.4",
"babel-preset-env": "^1.7.0",
"clean-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^5.0.0",
"jsx-loader": "^0.13.2",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"script-loader": "^0.7.2",
"style-loader": "^0.23.0",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "^1.1.2",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9",
"webpack-spritesmith": "^0.5.4",
"clean-webpack-plugin": "^2.0.0",
"copy-webpack-plugin": "^5.0.3",
}