banner
他山之石

他山之石

Webpack之打包体积优化

本文是我在使用 Webpack4 过程中的一些总结,介绍一些优化 Webpack 打包体积的方法。

image

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 的文件。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。