雖然在日常開發(fā)中,我們使用vue和react框架,它們的腳本架vue-cli和react-react-app會幫我們配置最基本的webpack,但我們了解webpack的一些配置,會讓我們在需要特定個(gè)性化配置時(shí)不至于手忙腳亂,能夠做進(jìn)一步優(yōu)化操作等。
在vue-cli中,要查看webpack的默認(rèn)配置,可以在項(xiàng)目中使用vue inspect命令查看;
在react-react-app 中,使用npm run eject將內(nèi)建的webpack文件都暴露出來
這篇將進(jìn)一步介紹webpack5一些高級配置:
- 多入口文件配置:
entry + output + html-webpack-plugin - css文件抽離和壓縮:
- 抽離:
mini-css-extract-plugin - 壓縮:
terser-webpack-plugin + optimize-css-assets-webpack-plugin
- 抽離:
- 抽離公共代碼和第三方代碼:
splitChunks - 懶加載:
import('xxx.js').then(res => {...}) - 識別
.jsx:@babel/preset-react - 識別
.vue:vue-loader
多入口文件配置
- 修改入口文件,改為對象形式
// webpack.common.js
// entry: path.join(srcPath, 'index'),
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
- 修改要輸出的模板配置,一般有多少個(gè)入口,就會設(shè)多少個(gè)出口模板,注意要寫上不同的
filename,chunks是個(gè)數(shù)組,用來說明引入哪些chunk,如果不寫,會默認(rèn)引入所有入口文件
// webpack.common.js
plugins: [
require('autoprefixer'),
new CleanWebpackPlugin(), // 清除之前的打包文件
new htmlWebpackPlugin({
// template: 'index.html',
template: path.join(rootPath, 'index.html'),
// 在這里還可以自定義參數(shù),在模板中,使用ejs方式 <%= htmlWebpackPlugin.options %>獲取自定義屬性
title: 'webpack multi demo1',
filename: 'index.html',
// 指明要引用哪些chunk,如不指定,會默認(rèn)引用所有入口文件
chunks: ['index'] // 只引用index.js
}),
new htmlWebpackPlugin({
template: path.join(rootPath, 'other.html'),
filename: 'other.html',
title: 'webpack multi demo2',
chunks: ['other'] // 指明引用other.js入口文件
})
]
- 修改出口文件
設(shè)置動(dòng)態(tài)的[name],用于匹配入口文件設(shè)置的文件名
// webpack.prod.js
output: {
// filename: 'bundle.[chunkhash].js',
filename: '[name].[chunckhash:8].js', // 從寫死改為以入口文件命名
path: path.join(__dirname, '..', 'dist') // 輸出目錄
}
抽離并壓縮css
- 抽離
上一篇已經(jīng)說明如何使用mini-css-extract-plugin來抽離css文件,抽離的好處是,當(dāng)只改變js文件時(shí),css文件因?yàn)?code>hash沒變,所以可以使用緩存,而不需要重新加載,訪問速度會快一些
// webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name]-[contenthash].css'
})
]
打包出來的css文件如下:
body {
background-color: yellow;
opacity: 0.7;
/* transform: rotate(45deg); */
/* background: url('../imgs/img002.jpg'); */
}
.test {
font-size: 18px;
font-weight: bold;
}
- 壓縮
單抽離是不夠的,我們在生產(chǎn)環(huán)境中還需要對其進(jìn)行壓縮,并盡可能讓打包的資源體積小,這也是加快速度的一種手段- 安裝:
npm i terser-webpack-plugin optimize-css-assets-webpack-plugin -D - 配置
- 安裝:
// webpack.prod.js
const TerserWebpackPlugin = require('terser-webpack-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const prodConfig = {
// ...
optimization: {
minimizer: [new TerserWebpackPlugin({}), new OptimizeCssAssetsWebpackPlugin()]
}
}
打包后,可以看到css文件被壓縮了,且注釋被去掉了
body{background-color:#ff0;opacity:.7}.test{font-size:18px;font-weight:700}
抽離公共代碼和第三方代碼
a文件引入了c文件,b也引入了c文件,那么在打包這a和b這兩文件時(shí),都要分別引入c文件,這操作不僅冗余,而且還浪費(fèi)性能,當(dāng)c文件很大時(shí),這個(gè)問題就越加明顯。
我們希望c文件只引入一次,就可以在多個(gè)文件中使用,這就是我們說的公共代碼。
第三方代碼指代的,它一般是指我們安裝引入的第三方庫,引入了后基本就不會變了,所以要與我們的業(yè)務(wù)代碼分離開來,這樣,我們修改業(yè)務(wù)代碼時(shí),第三方代碼的文件不會改變,從而可以命中緩存,加快加載速度。
抽離公共代碼和第三方代碼的配置,也比較簡單,就是在optimization中添加splitChucks的配置,可以看到,在webpack5除了入口文件外,已經(jīng)實(shí)現(xiàn)基本的按需引入,不需要我們在入口文件中再手動(dòng)設(shè)置指定的chunks
// webpack.prod.js
optimization: {
minimizer: [new TerserWebpackPlugin({}), new OptimizeCssAssetsWebpackPlugin()],
splitChunks: {
chunks: 'all', // 表示要分割的chunk類型:initial只處理同步的; async只處理異步的;all都處理
// 緩存分組
cacheGroups: {
// 第三方模塊
verdors: {
name: 'verdor', // chunk名稱
test: /node_modules/, // 設(shè)置命中目錄規(guī)則
priority: 1, // 優(yōu)先級,數(shù)值越大,優(yōu)先級越高
minSize: 0, // 小于這個(gè)大小的文件,不分割
minChunks: 1 // 最少復(fù)用幾次,這里意思是只要用過一次就分割出來
},
// 公共模塊
common: {
name: 'common',
minChunks: 2,
priority: 0,
minSize: 0,
minChunks: 2 // 只要引用過2次,就分割成公共代碼
}
}
}
}
設(shè)置兩個(gè)入口文件
// index.js
import _ from 'lodash'
import { sum } from './common/math'
console.log(sum(1,2))
console.log(_.each)
// other.js
import { sum } from './common/math'
console.log(sum(3,4))
plugins: [
new htmlWebpackPlugin({
template: path.join(rootPath, 'index.html'),
title: 'webpack multi demo1',
filename: 'index.html',
chunks: ['index'] // 引入index.js
}),
new htmlWebpackPlugin({
template: path.join(rootPath, 'other.html'),
filename: 'other.html',
title: 'webpack multi demo2',
chunks: ['other'] // 指明引用other.js入口文件
})
]
打包后,輸出模板中:
index.html引入了verdor,common,index
<!-- index.html -->
<script defer="defer" src="verdor.ab38c0e4.js"></script>
<script defer="defer" src="common.1be3f0b4.js"></script>
<script defer="defer" src="index.632b90fb.js"></script>
other.html中引入了common.js和other.js
<!-- other.html -->
<script defer="defer" src="common.1be3f0b4.js"></script>
<script defer="defer" src="other.cea083a6.js"></script>
懶加載
懶加載是webpack默認(rèn)支持的,不需要我們額外配置,使用方式:import('xxxx')
// common/dynamicData.js
export default {
msg: 'dynamic data'
}
// index.js
setTimeout(() => {
// 直接使用import導(dǎo)入即可,這樣加載的模塊,相當(dāng)于一個(gè)獨(dú)立的chunk存在
import('./common/dynamicData.js').then(res => {
console.log(res.default.msg);
})
}, 1000)
解析.jsx
- 安裝:
npm i @babel/preset-react -D - 在根目錄下創(chuàng)建
.babelrc
{
"presets": ["@babel/preset-react"]
}
解析.vue
- 安裝:
npm i vue-loader -D - 配置:
// webpack.common.js
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader'],
include: srcPath
}
]
},