美文网首页
记一次 webpack4+ 打包优化经历

记一次 webpack4+ 打包优化经历

作者: Mr无愧于心 | 来源:发表于2022-05-10 18:27 被阅读0次

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

    期间考虑要不要升一下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日志顶没,最好在命令行查看,别在编辑器中找

    打包速度优化

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

    • 开启多进程用的是thread-loader(happy-pack插件也可以用,但是已经不再维护了)
    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',
            ],
          },
        ......
    
    • 开启缓存,加快二次启动速度
      webpack 自带cache缓存,但是有更牛的hard-source-webpack-plugin
      hard-source-webpack-plugin 为模块提供中间缓存步骤(首次加载很慢,但以后的构建很快
    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
    
    plugins: [
        new HardSourceWebpackPlugin(),
    ],
    

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


    image.png

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

    相关文章

      网友评论

          本文标题:记一次 webpack4+ 打包优化经历

          本文链接:https://www.haomeiwen.com/subject/fwnlurtx.html