本文是我在使用 Webpack4 過程中的一些總結,介紹一些優化 Webpack 打包體積的方法。
Webpack 是當前大型專案的主流打包方案,自從將 react 的腳手架升級 Webpack4 以來,我也在不斷嘗試摸索 webpack 打包優化的一些方案。本文將從以下幾個方面,對於如何優化 Webpack 的打包體積,進行一些總結。
Bundle 分析#
webpack-bundle-analyzer 可以對打包輸出的 chunk 進行可視化分析,可以看到打包後每個模塊的大小,還能給出 gizp 壓縮後的大小,在生產環境中加載的模塊都是經過 gzip 壓縮過的,可以作為真實訪問的大小依據。
npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
Tree-Shaking#
Webpack4 在 mode 為 production 的情況下默認開啟了 Tree-Shaking,但在你的專案中可能會因為 babel 的原因導致它失效。
因為 Tree Shaking 這個功能是依賴於 ES2015 模塊系統中的靜態結構特性,例如 import 和 export。來找出未使用的代碼,所以如果你使用了 babel 插件的時候,如:babel-preset-env,它默認會將模塊打包成 commonjs,這樣就會讓 Tree Shaking 失效了。
所以需要配置 babel 不 transform modules。配置如下:
// .babelrc
{
"presets": [
["env", {
modules: false,
...
}]
]
}
url-loader、file-loader 配置優化#
url-loader 和 file-loader 一般用來處理背景圖,默認會把 css 中的背景圖打包成 Base64,當 css 中的背景圖過大,會造成打包後的 css 變得龐大,在單頁應用中,css 阻塞 DOM 渲染,造成首頁白屏時間過長。曾遇到過某專案 build 後 css 多達 4M,嚴重影響用戶體驗,解決辦法是配置 url-loader 的 limit 字段,超過一定字節則不打包成 base64。配置後重新打包後的 css 約 300k。
{
test: /\.(jpg|png|gif|svg)$/,
loader: 'url-loader',
include: path.join(__dirname, './src'),
exclude: /node_modules/,
options: {
limit: 8192,
name: 'images/[name].[hash:7].[ext]'
}
}
按需加載#
當我們使用 React、Vue 開發 SPA 應用時,webpack 默認會輸出一個 js 文件,這意味這首屏渲染的時候,會加載完所有的頁面 js,js 體積過大極大影響首屏渲染速度,按需加載是一個不錯的選擇,對每一個路由對應的頁面打包成單獨的 chunk。目前最流行的辦法是使用動態 import 的方法。在 react 專案中,你可以使用 react-loadable 這個第三方庫。
import Loadable from 'react-loadable';
import Loading from './my-loading-component';
const LoadableComponent = Loadable({
loader: () => import('./my-component'),
loading: Loading,
});
<Route path="/user" component={LoadableComponent} />
webpack 會自動拆分 chunk,首屏渲染時,只會加載對應的 chunk,提高了渲染速度。
按需打包 mement、lodash、antd#
moment、lodash、antd 是使用頻率很高的第三方庫,但是庫本身的體積很大,專案中又不會用到全部的功能,全部引入會造成打包後的體積過大。
antd 可以使用 babel-plugin-import 來進行按需加載,只需要安裝 babel-plugin-import 然後配置.babelrc 文件:
{
"plugins": [
[
"import", {
"libraryName": "antd",
"style": true
}
]
]
}
babel-plugin-import 目前已經可以使用於 antd、antd-mobile、lodash 等庫。
lodash 也可以使用 babel-plugin-import 來進行按需加載,也可以使用 babel-plugin-lodash 來按需加載 lodash。
npm i --save lodash
npm i --save-dev babel-plugin-lodash @babel/cli @babel/preset-env
// .babelrc
{
"plugins": ["lodash"],
"presets": [["@babel/env", { "targets": { "node": 6 } }]]
}
moment 會將所有本地化內容和核心功能一起打包,打包出來的 moment 依賴包括了許多不需要的 locale 文件。因此我們需要對 moment 的 locale 文件進行按需打包。
你可以使用 IgnorePlugin 在打包時忽略本地化內容:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/);
也可以使用 webpack 的上下文替換插件 (ContextReplacementPlugin),允許覆蓋查找規則,通過正則篩選指定的文件。
只需要在 webpack 的配置文件 plugins 處增加如下代碼即可:
plugins: [
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/zh-cn/,
),
],
這裡通過正則匹配了 moment/locale 下的名為 zh-cn 的文件。