webpack优化

248 阅读2分钟

优化打包速度

通过 speed-measure-webpack-plugin 测量你的 webpack 构建期间各个阶段花费的时间。

1. 模块按需编译

参考链接:zhuanlan.zhihu.com/p/137120584

2. 缩小文件的搜索范围(配置include exclude alias noParse extensions)

  • resolve.alias: 但我们代码中出现import * from 'vue'时,webpack会采用向上递归搜索的方式去node_modules目录下找。为了减少搜索范围我们可以直接告诉webpack去哪个路径下查找。也就是别名(alias)的配置。
  • include exclude:可以减少webpack loader的搜索转换时间。
  • noParse:当我们代码中使用到了import $ from jquery时,webpack会去解析jquery这个库是否有依赖其他的包。但是我们对类似jquery这类依赖包,一般会认为不会引用其他的包(特殊除外,自行判断)。增加noParse属性,告诉webpack不必解析,以此增加打包的速度。
  • resolve.extensions:webpack会根据extensions定义的后缀查找文件(频率较高的文件类型优先写在前面)。

3. 合理配置mode参数与devtool参数

mode可以设置development production两个参数

如果没有设置,webpack4会将mode的默认值设置为production

production模式下会进行tree shaking(去除无用代码)和uglifyjs(代码压缩混淆)

4. 缓存编译结果

我们每次执行苟安都会把所有的文件都重复一遍,这样的重复工作是否可以被缓存下来呢?答案是可以的,目前大部分的loader都提供了cache配置项。比如在babel-loader中,可以通过设置cacheDirectory来开启缓存,babel-loader?cacheDirectory=true就会将每次的编译结果写进硬盘文件(默认是在项目根目录下的node_modules/.cache/babel-loader目录中,当然你也可以自定义)

但如果loader不支持缓存呢?我们也有办法,我们可以通过cache-loader,他所做的事件很简单,就是babel-loader开启cache后做的事情,将loader的编译结果写入硬盘缓存,再次构建会先比较一下,如果文件较之前的没有发生变化则会直接使用缓存。

5. 抽离第三方模块

DllPlugin和DllReferencePlugin用某种方法实现了拆分bundles,同时还大幅度提升了构建的速度。

6. 优化解析时间——开启多线程打包

运行在Node.js之上的webpack是单线程模式的,也就是说,webpack打包只能串行处理文件,当webpack需要打包大量文件时,打包时间就会比较漫长。

  1. thread-loader (webpack4官方推荐) 把这个loader放置在其他loader之前,放置在这个loader之后的loader就会在一个单独的worker池里运行,一个worker就是一个nodejs进程, 每个单独进程的处理时间上线为600ms, 各个进程的数据交换也会限制在这个时间内。
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        // 创建一个 js worker 池
        use: [ 
          'thread-loader',
          'babel-loader'
        ] 
      },
      {
        test: /\.s?css$/,
        exclude: /node_modules/,
        // 创建一个 css worker 池
        use: [
          'style-loader',
          'thread-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[name]__[local]--[hash:base64:5]',
              importLoaders: 1
            }
          },
          'postcss-loader'
        ]
      }
      // ...
    ]
    // ...
  }
  // ...
}

注意:thread-loader 放在了 style-loader 之后,这是因为 thread-loader 后的 loader 没法存取文件也没法获取 webpack 的选项设置。

官方上说每个 worker 大概都要花费 600ms ,所以官方为了防止启动 worker 时的高延迟,提供了对 worker 池的优化:预热

// ...
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
};

threadLoader.warmup(jsWorkerPool, ['babel-loader']);
threadLoader.warmup(cssWorkerPool, ['css-loader', 'postcss-loader']);


module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: jsWorkerPool
          },
          'babel-loader'
        ]
      },
      {
        test: /\.s?css$/,
        exclude: /node_modules/,
        use: [
          'style-loader',
          {
            loader: 'thread-loader',
            options: cssWorkerPool
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[name]__[local]--[hash:base64:5]',
              importLoaders: 1
            }
          },
          'postcss-loader'
        ]
      }
      // ...
    ]
    // ...
  }
  // ...
}

  1. HappyPack已不再继续维护,不推荐使用

7. 优化压缩时间-开启多进程压缩

webpack4中webpack.optimize.UglifyJsPlugin已被废弃

也不推荐使用ParallelUglifyPlugin,项目基本处于没人维护的阶段

webpack4默认内置使用terser-webpack-plugin插件压缩优化代码,而该插件使用terser来缩小JavaScript。

terser启动多进程

使用多进程并行运行来提高构建速度,并发运行的默认数量为os.cpus.length - 1。

module.exports = {
    optimization: { 
        minimizer: [ 
            new TerserPlugin({ 
                parallel: true,
            }), 
        ], 
    }, 
};
  1. 使用高本版的wepack、loader、plugin

打包体积优化

代码分离

把代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的hundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。 常用的代码分离方法有三种:

  • 入口起点:使用entry配置手动的分离代码
  • 防止重复:使用SplitChunksPlugin去重和分类chunk
  • 动态导入:通过模块的内联函数调用来分离代码

tree shaking

其他