基于 webpack 优化老项目 👻

4,436 阅读5分钟

项目技术栈是 dva + antd-mobile,由于是早期项目,项目里 webpack 版本用的是 3.5.6,所以有些配置我会加上 webpack 4.x 版本的写法。有些 plugin 或者相关配置列出的较少或者讲解比较浅显,可以到 webpack 官网或者对应的 plugin Github 上查看详细的参数配置哟。有不足之处还请指正 🙏。

Roadhog 官方介绍:Roadhog 是一个包含 devbuildtest 的命令行工具,他基于 react-dev-utils,和 create-react-app 的体验保持一致。你可以想象他为可配置版的 create-react-app

Roadhog是有一些特定的参数去写相关的打包配置,具体可参考 Roadhog

但是想要添加一些 Roadhog 给定参数没有的配置,比如打包分析 plugin webpack-bundle-analyzer,还是需要单独创建一个 webpack.config.js 文件去配置。(没有找到直接在 .webpackrc.js 文件中配置的方法,有解决方案的老兄还请告知呀 👀)

优化从下面几个方向进行:

  • 首先添加打包分析 - analyzer
  • 减小打包文件体积
  • 提升打包速度
  • 提升打包后的文件加载速度
  • 针对一些较大模块的处理
  • CDN 引入部分基础依赖

打包分析 - webpack-bundle-analyzer

这个插件是分析项目打包后各个模块文件的大小

npm i -D webpack-bundle-analyzer

具体参数可参考:配置详情及参数链接

/* webpack.config.js */
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

···
plugin: [
	···
  new BundleAnalyzerPlugin()
]
···

/* package.json */
···
"scripts": {
  ···
  "analyz": "NODE_ENV=production npm_config_report=true npm run build"
},
···

运行:npm run analyz, 默认地址 http://127.0.0.1:8888/

analyz
analyz分析部分页面

总体的减小打包体积

UglifyJsPlugin

删除文件里的注释和 console,删除冗余代码,减小打包体积的目的。还可以删除 debugger,设置 cache 缓存等配置。

webpack 3.x 版本:通过 webpack.optimize.UglifyJsPlugin 设置

/* webpack.config.js */
···
plugins: [
  ···
  new webpack.optimize.UglifyJsPlugin({
      cache: true,
      compress: {
        warnings: false, // 是否在UglifyJS删除没有用到的代码时输出警告信息,默认为false
        drop_debugger: true, //自动删除debugger
        drop_console: true, //自动删除console.log
      },
  }),
  ···
]

webpack 4.x 版本webpack 4.x 版本移除了 webpack.optimize.UglifyJsPlugin 的使用,通过配置 optimization.minimizeoptimization.minimizer 来自定义压缩相关的操作。

npm i -D uglifyjs-webpack-plugin

/* webpack.config.js */

···
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
    		// 相关配置
    	})
    ],
  },
};

提升打包速度

ParalleUglifyPlugin

提升编译打包时 js 压缩速度,还可设置打包时删除注释和 console 等代码,可以合并上文中的 UglifyJsPlugin

npm i -D webpack-parallel-uglify-plugin

/* webpack.config.js */

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
plugin: [
	···
  new ParallelUglifyPlugin({
    cacheDir: '.cache/', // 用作缓存的可选绝对路径。如果未提供,则不使用缓存。
    uglifyJS: {
      output: {
        comments: false, // 是否保留代码中的注释,默认为保留
      },
      warnings: false, // 是否在UglifyJS删除没有用到的代码时输出警告信息,默认为false
      compress: {
        drop_console: true, // 是否删除代码中所有的console语句,默认为false
        collapse_vars: true, // 是否内嵌虽然已经定义了,但是只用到一次的变量, 默认值false
        reduce_vars: true, // 是否提取出现了多次但是没有定义成变量去引用的静态值,默认为false
      },
    },
  }),
  ···
]

happypack

开启多进程编辑,提升 loader 解析速度

npm i -D happypack

/* webpack.config.js */

const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const createHappyPlugin = (id, loaders) =>
new HappyPack({
  id: id,
  loaders: loaders,
  threadPool: happyThreadPool,
  verbose: process.env.HAPPY_VERBOSE === '1', // make happy more verbose with HAPPY_VERBOSE=1
});

plugin: [
	···
  createHappyPlugin('happy-babel', [
    {
        loader: 'babel-loader',
        options: {
          babelrc: true,
          cacheDirectory: true, // 启用缓存
        },
      },
    ])
  ···
]

提升加载速度

压缩打包后的文件

减小文件体积,节省服务器的网络带宽,加速网站打开速度。经过服务器压缩,客户端浏览器快速加压的原理,可以很大的减少网站的流量。

压缩方式有两个:一种是 zip 压缩,一种是 gzip 压缩。

  • zip-webpack-plugin - zip 压缩,会把所有的文件全部压缩到一个 zip 文件中

    npm i -D zip-webpack-plugin

/* webpack.config.js */

const ZipPlugin = require('zip-webpack-plugin');
···
new ZipPlugin({
  filename: 'index.zip',
}),
···

zip
zip压缩后的文件截图

  • compression-webpack-plugin - gzip 压缩,单独压缩每个文件

    npm i -D compression-webpack-plugin

/* webpack.config.js */

const CompressionPlugin = require('compression-webpack-plugin');
···
new CompressionPlugin({
  filename: '[path].gz[query]',
  algorithm: 'gzip',
  test: /\.js$|\.css$/,
  threshold: 10240,
  minRatio: 0.8,
}),
···

gzip
gzip压缩后的文件截图

CommonChunkPlugin - 代码分割打包

抽取入口文件依赖的类库打包到 vendor 文件中,首次加载后存在缓存中,此后的加载会在浏览器缓存中读取,提高加载速度。

webpack 3.x 版本

/* webpack.config.js */

···

// 把所有的依赖的类库打包到 vendor 文件中
new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module) {
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
    );
  }
})
···

webpack 4.x 版本

/* webpack.config.js */

···
optimization: {
	splitChunks: {
      	    chunks: 'all', // async对异步代码做分割/all同步和异步都分割/initial对同步代码做分割
            minSize: 30000, // import引入的组件或者文件大于300000字节=30kb,才会做代码分割
            minChunks: 1, // 当一个模块被用了多少次后就进行代码分割
            ···
        }
}
···

css 抽离 JS 文件,单独打包

npm i -D extract-text-webpack-plugin

/* webpack.config.js */

const ExtractTextPlugin = require('extract-text-webpack-plugin');
···
plugins: [
	new ExtractTextPlugin('[name].[contenthash].css')
]
···

针对大文件的处理

analyz 分析可以看出 momentcore-jscrypto-js 文件过大,以下针对这三个模块进行处理。

moment - 解决 moment 文件过大有两个方法

  • IgnorePlugin 插件
/* webpack.config.js */

plugins: [
  ···
  // 忽略 moment.js的所有本地文件
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ···
]

需要手动引入 locale 文件:


const moment = require('moment');
require('moment/locale/ja');

moment.locale('ja');
...
  • ContextReplacementPlugin 插件
/* webpack.config.js */

plugin: [
  ···
  // 只加载 `moment/locale/ja.js` 和 `moment/locale/it.js`
  new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /ja|it/)
  ···
]

不需要手动引入 locale 文件

const moment = require('moment');
// 不需要
moment.locale('ja');
...

core-js

我在项目中并没有用到 core-js,搜索插件得知这个是 @babel-polyfill 用的库,打包的时候一并打包了进来。 把 babel-polyfill 提取出来 CDN 引入 polyfill

/* webpack.config.js */
···
externals: {
  'babel-polyfill': 'window',
}
···

/* index.html script引入 */
<script src="https://cdn.bootcss.com/babel-polyfill/6.22.0/polyfill.min.js"></script>

crypto-js

这个是加解密相关的库

之前的引用方式是直接引入 crypto-js 整个方法(大概有四十多个文件方法,具体有多少方法可自行查看 node_modules 下的 crypto-js 文件),这样会把很多无用的方法也加载进来,增加打包体积:

const CryptoJS = require('crypto-js');

// 方法调用
CryptoJS.HmacSHA256
CryptoJS.enc.Base64
CryptoJS.enc.Utf8

优化使用相关方法,使用哪个就直接引入 crypto-js 下的该方法的文件:

const CryptoJS_HmacSHA256 = require('crypto-js/hmac-sha256');
const CryptoJS_Base64 = require('crypto-js/enc-base64');
const CryptoJS_Utf8 = require('crypto-js/enc-utf8');

CDN 引入基础依赖

提取 ReactReactDomCDN 引入

/* webpack.config.js */
···
externals: {
  'react': 'React',
  'react-dom': 'ReactDOM',
}
···

/* index.html script引入 */
<script src="https://unpkg.com/react@16.5.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.5.2/umd/react-dom.production.min.js"></script>

END

老项目优化实属不易,整理一下优化方法,记录一下。

其实 react-count-animation 这个模块文件也挺大的,但是没有找到优化的方案,有优化方法的兄弟们望告知呀 🙏 。