记一次 webpack4+ 打包优化经历

2022-05-10  本文已影响0人  Mr无愧于心

因为项目过于庞大,有组内的小伙伴有人抱怨项目启动太慢啦。所以这个事得办。抓紧办。

期间考虑要不要升一下webpack5,其实webpack5在其他项目中已经升级过,对于项目的打包速度没有什么提升,只是内置了一些类似于缓存hard-source-webpack-plugin插件、file-loader等,ps: 我对webpack5的关注点在于模块联邦,实现不同项目间组件复用、微前端这些。。。

所以就拿着webpack4开搞吧。

1. 包体积

ps: 包的体积减小自然会使构建的速度有所提升

包体积分析插件 BundleAnalyzerPlugin

BundleAnalyzerPlugin配置入webpack中会自动生成打开包分析页面localhost:8888;

plugins: [
    new HtmlWebpackPlugin({ template: './index.html' }),
    // HMR
    new webpack.HotModuleReplacementPlugin(),
    // new CleanWebpackPlugin(['public']),
    new CopyWebpackPlugin([{
      from: './src/assets',
    }]),
    new MiniCssExtractPlugin({
      filename: 'vender.css',
      chunkFilename: '[name].css',
    }),
    new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn/),
    new WebpackBar({
      color: '#0096f5',
      name: 'frontend',
    }),
    new OpenBrowserPlugin({ url: 'http://localhost:7878' }),
    new BundleAnalyzerPlugin({ analyzerHost: 'localhost' }),
  ],

启动项目的同时 会打开这样一个页面


拆包前的包体积分析

当然我们不想每次启动项目就展开包体积分析,所以我们给他一个其他的启动命令,只有运行这个启动命令时再进行包分析

"scripts": {
    "visu": "NODE_ENV=visu node script/prod-build",
},
// 运行 yarn visu的时候将node环境设为visu 并运行script文件夹下的prod-build文件

// 那么主要的逻辑就在prod-build.js文件中了

// prod-build.js主要是修改node变量、合并BundleAnalyzerPlugin和webpack的其他插件、运行webpack。
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const browserConfig = require('../webpack.prod');// 拿到webpack的主要配置项

if (process.env.NODE_ENV === 'visu') { //当环境是visu的时候添加BundleAnalyzerPlugin插件
  browserConfig.plugins.push(new BundleAnalyzerPlugin({ analyzerHost: 'localhost' }));
}

// Webpack compile in a try-catch
function compile(config) {
  let compiler;
  try {
    compiler = webpack(config);
  } catch (e) {
    console.log('error');
    process.exit(1);
  }
  return compiler;
}

const clientCompiler = compile(browserConfig);// 启动webpack编译

clientCompiler.run((err) => {
  if (err) {
    console.error(err);
  }
});
// 这样就通过yarn vis能够给编译的webpack加入BundleAnalyzerPlugin
// 命令运行时自动打开包分析页面了

有了包分析,我们就可以进行包的拆分了
拆包好处太多了:打包速度快、公共包拎出来避免每次编译、按需加载、首评打开速度提升。。。

拆包
// webpack4和5都可以通过optimization的splitChunks来做
// 其实也可以用dllplugin(webpack3时代用的比较多这次就不用了,因为有更好的)
optimization: {
    splitChunks: {
      minSize: 30720, // 超过30K分包
      maxSize: 102400, // 分包最大100K超出再分包
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      cacheGroups: {
        'react-vendor': {
          test: module => /react/.test(module.context) || /classnames/.test(module.context) || /mobx/.test(module.context),
          priority: 4,
          reuseExistingChunk: true,
          name: 'react',
        },
        'antd-vendor': {
          test: module => (/antd?/.test(module.context)),
          priority: 3,
          reuseExistingChunk: true,
          name: 'antd',
        },
        'xlsx-vendor': {
          test: /xlsx/,
          priority: 1,
          reuseExistingChunk: true,
          name: 'xlsx',
        },
        'lodash-vendor': {
          test: /lodash/,
          priority: 2,
          reuseExistingChunk: true,
          name: 'lodash',
        },
        vendors: {
          name: 'chunk-vendors', // 优先级小于output.filename
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },

这样在包分析页就可以看到,很多的模块的公共的包被抽离了出来,且没有了大体积的文件
(在这个项目中 体积由30.2M变成了6.74M,优化到原来的四分之一多)


拆包后的包体积分析

2. 构建速度

构建速度提升的点可太多了,代码写质量都会有影响,在webpack中构建的速度很受打包文件数量和编译的loader限制。
要提升构建的速度,我们首先得知道当前webpack打包的时常是多少,这里用到的是speed-measure-webpack-plugin插件

打包速度分析(speed-measure-webpack-plugin)
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({ // smp.wrap()包一下webpack的配置即可
  entry: ['src/index.js'],
  output: {
    path: resolve('public'),
    filename: 'vender.js',
    chunkFilename: '[name].js',
    publicPath: '/',
  },
  resolve: {
    alias: {
      src: resolve('src'),
      assets: resolve('src/assets'),
      components: resolve('src/components'),
      style: resolve('src/static/style'),
    },
  },
。。。。
打包速度优化前

以上这个日志可能被其他webpack日志顶没,最好在命令行查看,别在编辑器中找

打包速度优化

打包速度优化主要用的是开启多进程、开启缓存功能

const threadLoader = require('thread-loader');

const jsWorkerPool = {
  // options
  // 产生的 worker 的数量,默认是 (cpu 核心数 - 1)
  // 当 require('os').cpus() 是 undefined 时,则为 1
  workers: 2,
  // 闲置时定时删除 worker 进程
  // 默认为 500ms
  // 可以设置为无穷大, 这样在监视模式(--watch)下可以保持 worker 持续存在
  poolTimeout: 2000,
};
const cssWorkerPool = {
  // 一个 worker 进程中并行执行工作的数量
  // 默认为 20
  workerParallelJobs: 2,
  poolTimeout: 2000,
};
// 可以通过预热 worker 池(worker pool)来防止启动 worker 时的高延时
threadLoader.warmup(jsWorkerPool, ['babel-loader']);// js文件的预热
threadLoader.warmup(cssWorkerPool, ['css-loader', 'sass-loader']);// css文件的预热


module.exports = smp.wrap({
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /(node_modules)/,
        use: [
          {
            loader: 'thread-loader',// 开启多个work,使用预热的配置
            options: jsWorkerPool,
          },
          {
            loader: 'babel-loader',
            options: {
              babelrc: true,
            },
          },
        ],
      },
      {
        test: /\.(scss|css)$/,
        use: [
          'css-hot-loader',
          MiniCssExtractPlugin.loader,
          {
            loader: 'thread-loader',// 开启多个work,使用预热的配置
            options: cssWorkerPool,
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            },
          },
          'sass-loader',
        ],
      },
    ......
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

plugins: [
    new HardSourceWebpackPlugin(),
],

我们来看下此时的打包速度


image.png

还有其他的一些优化类似于loader的exclude/include的使用、extends的合理使用、alias的使用。。。。巴拉巴拉的。。。就不多赘述了

上一篇下一篇

猜你喜欢

热点阅读