Webpack构建速度优化实战:从分析到并行打包的完整指南

作者:缓存笔记

时间:2026-10-23

浏览:584

本文详细介绍Webpack构建速度的优化策略。首先通过命令行参数或可视化工具分析构建耗时,定位瓶颈。接着从Loader缓存、模块解析路径及后缀配置入手进行基础优化。随后深入讲解利用DllPlugin将第三方库抽离为动态链接库,以及使用HappyPack和ParallelUglifyPlugin实现Loader和代码压缩的并行处理,显著降低构建时间。

文章目录

Webpack构建分析工具与配置

如何输出Webpack构建分析

生成记录Webpack构建详细信息的JSON文件:webpack --profile --json > stats.json

  1. 使用--profile参数:记录构建过程中的耗时信息
  2. 使用--json参数:以JSON格式输出构建结果,最终仅生成一个包含所有构建信息的JSON文件

如何可视化查看Webpack构建分析结果?获取到stats.json文件后,如何进行直观的可视化查看?

  1. 方案一:使用在线Web应用Webpack Analyse,上传stats.json文件即可进行分析;
  2. 方案二:安装webpack-bundle-analyzer工具npm i -g webpack-bundle-analyzer,生成stats.json后,直接在文件夹目录执行webpack-bundle-analyzer命令,浏览器会自动打开对应网页并展示构建分析webpack-bundle-analyzer stats.json -p 8888。文档地址:webpack-bundle-analyzer
  3. webpack-dashboard是一款统计和优化Webpack日志的工具,以表格形式展示日志信息,包括构建过程、状态及涉及的模块列表。
  4. jarvis是一款基于webpack-dashboard的Webpack性能分析插件,结果在浏览器中显示,比webpack-bundle-analyzer更美观清晰。GitHub文档地址

npm i -D webpack-jarvis

配置webpack.config.js:


const Jarvis = require("webpack-jarvis");

plugins: [
 new Jarvis({
  watchOnly: false,
  port: 3001 // optional: set a port
 })
];

port:监听的端口,默认1337,监听面板将监听此端口,通常访问http://localhost:port/;

host:域名,默认localhost,不限制域名。

watchOnly:仅监听编译阶段。默认为true,如果设为false,jarvis不仅在编译阶段运行,编译完成后还保持运行状态。

界面显示构建时间为:Time: 11593ms(作为优化前后的时间对比)

Webpack构建分析工具与配置

Webpack基础配置优化策略

Webpack配置优化

Webpack启动时从配置的Entry出发,解析文件中的导入语句,再递归解析。

对于导入语句,Webpack会执行以下操作:

  1. 根据导入语句寻找对应的要导入的文件;
  2. 根据要导入的文件后缀,使用配置中的Loader处理文件(如ES6需要使用babel-loader处理)

针对以上两点,可以优化查找途径

1、优化Loader配置

Loader处理文件的转换操作非常耗时,因此需要让尽可能少的文件被Loader处理


{
  test: /\.js$/,
  use: [
    'babel-loader?cacheDirectory',//开启转换结果缓存
  ],
  include: path.resolve(__dirname, 'src'),//只对src目录中文件采用babel-loader
  exclude: path.resolve(__dirname,' ./node_modules'),//排除node_modules目录下的文件
},

2、优化resolve.modules配置

resolve.modules用于配置Webpack去哪些目录下寻找第三方模块,默认是['node_modules']。但是,它会先去当前目录的./node_modules查找,没有的话再去../node_modules,最后到根目录;

因此,当安装的第三方模块都放在项目根目录时,就没有必要按照默认的一层层查找,直接指明存放的绝对位置


resolve: {
    modules: [path.resolve(__dirname, 'node_modules')],
  }

3、优化resolve.extensions配置

在导入没带文件后缀的路径时,Webpack会自动带上后缀去尝试询问文件是否存在,而resolve.extensions用于配置尝试后缀列表;默认为extensions:['js','json'];

即当遇到require('./data')时,Webpack会先尝试寻找data.js,没有再去找data.json;如果列表越长,或者正确的后缀越往后,尝试的次数就会越多;

因此在配置时,为提升构建优化需遵守:

  1. 频率出现高的文件后缀优先放在前面;
  2. 列表尽可能的小;
  3. 书写导入语句时,尽量写上后缀名

因为项目中用的jsx较多,所以配置extensions: [".jsx",".js"],

基本配置后查看构建速度:Time: 10654ms;配置前为Time: 11593ms

DllPlugin动态链接库优化

使用DllPlugin优化

在使用Webpack进行打包时,对于依赖的第三方库,如react、react-dom等这些不会修改的依赖,可以让它和业务代码分开打包;

只要不升级依赖库版本,之后Webpack就只需要打包项目业务代码,遇到需要导入的模块在某个动态链接库中时,就直接去其中获取;而不用再去编译第三方库,这样第三方库就只需要打包一次。

接入需要完成的事:

  1. 将依赖的第三方模块抽离,打包到一个个单独的动态链接库中
  2. 当需要导入的模块存在动态链接库中时,让其直接从链接库中获取
  3. 项目依赖的所有动态链接库都需要被加载

接入工具(Webpack已内置)

DllPlugin插件:用于打包出一个个单独的动态链接库文件;

DllReferencePlugin:用于在主要的配置文件中引入DllPlugin插件打包好的动态链接库文件

配置webpack_dll.config.js构建动态链接库


const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');

module.exports = {
  mode: 'production',
  entry: {
    // 将React相关模块放入一个动态链接库
    react: ['react','react-dom','react-router-dom','react-loadable'],
    librarys: ['wangeditor'],
    utils: ['axios','js-cookie']
  },
  output: {
    filename: '[name]-dll.js',
    path: path.resolve(__dirname, 'dll'),
    // 存放动态链接库的全局变量名,加上_dll_防止全局变量冲突
    library: '_dll_[name]'
  },
  // 动态链接库的全局变量名称,需要可output.library中保持一致,也是输出的manifest.json文件中name的字段值
  // 如react.manifest.json字段中存在"name":"_dll_react"
  plugins: [
    new DllPlugin({
      name: '_dll_[name]',
      path: path.join(__dirname, 'dll', '[name].manifest.json')
    })
  ]
}

在webpack.pro.config.js中使用


const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
...
plugins: [
  // 告诉webpack使用了哪些动态链接库
    new DllReferencePlugin({
      manifest: require('./dll/react.manifest.json')
    }),
    new DllReferencePlugin({
      manifest: require('./dll/librarys.manifest.json')
    }),
    new DllReferencePlugin({
      manifest: require('./dll/utils.manifest.json')
    }),
]

注意:在webpack_dll.config.js文件中,DllPlugin中的name参数必须和output.library中的一致;因为DllPlugin的name参数影响输出的manifest.json的name;而webpack.pro.config.js中的DllReferencePlugin会读取manifest.json的name,将值作为从全局变量中获取动态链接库内容时的全局变量名

执行构建

webpack --progress --colors --config ./webpack.dll.config.js

webpack --progress --colors --config ./webpack.prod.js

html中引入dll.js文件

构建时间对比:["11593ms","10654ms","8334ms"]

HappyPack与并行压缩优化

HappyPack并行构建优化

核心原理:将Webpack中最耗时的loader文件转换操作任务,分解到多个进程中并行处理,从而减少构建时间。

HappyPack

接入HappyPack

安装:npm i -D happypack

重新配置rules部分,将loader交给happypack来分配:


const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({size: 5}); //构建共享进程池,包含5个进程
...
plugins: [
  // happypack并行处理
  new HappyPack({
    // 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应
    id: 'babel',
    loaders: ['babel-loader?cacheDirectory'],//默认设置loader处理
    threadPool: happyThreadPool,//使用共享池处理
  }),
  new HappyPack({
    // 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应
    id: 'css',
    loaders: [
      'css-loader',
      'postcss-loader',
      'sass-loader'],
      threadPool: happyThreadPool
  })
],
module: {
  rules: [
  {
    test: /\.(js|jsx)$/,
    use: ['happypack/loader?id=babel'],
    exclude: path.resolve(__dirname,' ./node_modules'),
  },
  {
    test: /\.(scss|css)$/,
    //使用的mini-css-extract-plugin提取css此处,如果放在上面会出错
    use: [MiniCssExtractPlugin.loader,'happypack/loader?id=css'],
    include:[
      path.resolve(__dirname,'src'),
      path.join(__dirname, './node_modules/antd')
    ]
  },
}

参数:

threads:代表开启几个子进程去处理这一类文件,默认是3个;

verbose:是否运行HappyPack输出日志,默认true;

threadPool:代表共享进程池,即多个HappyPack示例使用一个共享进程池中的子进程去处理任务,以防资源占有过多

代码压缩用ParallelUglifyPlugin代替自带的UglifyJsPlugin插件

自带的JS压缩插件是单线程执行的,而webpack-parallel-uglify-plugin可以并行执行

配置参数:

  • uglifyJS: {}:用于压缩ES5代码时的配置,Object类型
  • test: /.js$/g:使用正则去匹配哪些文件需要被ParallelUglifyPlugin压缩,默认是/.js$/
  • include: []:使用正则去包含被压缩的文件,默认为[]。
  • exclude: []:使用正则去包含不被压缩的文件,默认为[]
  • cacheDir: '':缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回,默认不会缓存,开启缓存设置一个目录路径
  • workerCount: '':开启几个子进程去并发执行压缩。默认是当前运行电脑的CPU核数减去1
  • sourceMap: false:是否为压缩后的代码生成对应的Source Map,默认不生成

...
minimizer: [
  // webpack:production模式默认有配有js压缩,但是如果这里设置了css压缩,js压缩也要重新设置,因为使用minimizer会自动取消webpack的默认配置
  new optimizeCssPlugin({
    assetNameRegExp: /\.css$/g,
    cssProcessor: require('cssnano'),
    cssProcessorOptions: { discardComments: { removeAll: true } },
    canPrint: true
  }),
  new ParallelUglifyPlugin({
    cacheDir: '.cache/',
    uglifyJS:{
      output: {
      // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
        beautify: false,
    //是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
        comments: false
      },
      compress: {
      //是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出
        warnings: false,
      //是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
        drop_console: true,
      //是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 1, 默认为否
        collapse_vars: true,
      }
    }
}),
]

构建结果对比:["11593ms","10654ms","8334ms","7734ms"]

整体构建速度从12000ms降到现在的8000ms

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章