React + Webpack 构建打包优化实战指南

作者:安全边界

时间:2026-10-23

浏览:311

本文详细解析 React 项目中 Webpack 的构建优化策略。通过 babel-react-optimize 清理 PropTypes,利用 DllPlugin 和 HappyPack 提升编译速度,并借助 CommonsChunkPlugin 进行代码分割与 chunkhash 管理。此外,介绍使用 webpack-bundle-analyzer 分析体积,以及启用 gzip 压缩减小传输大小,旨在提升页面加载性能与开发体验。

文章目录

本文主要探讨在 React 结合 Webpack 技术栈下的构建与打包优化方案,供开发者参考。

React 代码优化策略

利用 babel-react-optimize 插件对 React 相关代码进行专项优化处理。

审查项目中未被实际引用的库,移除多余的 import 引入语句。

对所用类库实行按需打包策略,例如 lodash 和 echart 等第三方库。

针对 lodash 库,推荐使用 babel-plugin-lodash 插件进行优化。

需要特别注意的是

babel-react-optimize 内部集成了 babel-plugin-transform-react-remove-prop-types 插件。若代码中未引用组件的 PropTypes,则无影响;但若使用了 PropTypes,该插件可能导致异常。

具体参考

https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types#is-it-safe

Webpack 构建打包优化

Webpack 构建过程中的问题主要集中在以下两点:

  1. Webpack 构建速度较慢
  2. Webpack 打包生成的文件体积过大

提升 Webpack 构建速度

针对 Webpack 构建速度慢的问题

可引入 Webpack.DDLPlugin 和 HappyPack 来提升构建效率。具体参考小铭关于 DMP DDLPlugin 的文档,内容如下:

Webpack.DLLPlugin

新增 webpack.dll.config.js 配置文件。主要利用 DllPlugin 插件将部分第三方资源独立打包,并生成 manifest.json 配置文件。

这样在组件更新时,无需重新构建这些第三方资源。

  1. 同时独立配置 dll/vendors.js 文件,供 webpack.dll.config.js 使用。
  2. 修改 package.json

在 scripts 字段中添加命令:"dll": "webpack --config webpack.dll.config.js --progress --colors ", 。

执行 npm run dll 后,会在 dll 目录下生成 vendor-manifest.json 和 vendor.dll.js 两个文件。

在 webpack.dev.config.js 配置文件中,加入 DllReferencePlugin 插件,并指定 vendor-manifest.json 文件路径。


new webpack.DllReferencePlugin({
 context: join(__dirname, 'src'),
 manifest: require('./dll/vendor-manifest.json')
})

修改 html 模板


<% if(htmlWebpackPlugin.options.NODE_ENV ==='development'){ %>
 <script src="dll/vendor.dll.js"></script>
<% } %>

注意,需在 htmlWebpackPlugin 插件中配置 NODE_ENV 参数。

Happypack

通过多线程处理和缓存机制提升 rebuild 效率,参考地址:https://github.com/amireh/happypack

在 webpack.dev.config.js 中为不同资源创建多个 HappyPack 实例,例如 js 一个、less 一个,并设置好对应的 id。


new HappyPack({
 id: 'js',
 threadPool: happyThreadPool,
 cache: true,
 verbose: true,
 loaders: ['babel-loader?babelrc&cacheDirectory=true'],
}),
new HappyPack({
 id: 'less',
 threadPool: happyThreadPool,
 cache: true,
 verbose: true,
 loaders: ['css-loader', 'less-loader'],
})

在 module.rules 中配置 use 为 happypack/loader,并设置 id。


{
 test: /\.js$/,
 use: [
 'happypack/loader?id=js'
 ],
 exclude: /node_modules/
}, {
 test: /\.less$/,
 loader: extractLess.extract({
 use: ['happypack/loader?id=less'],
 fallback: 'style-loader'
 })
}

减小打包文件体积

减少 Webpack 打包后的文件体积大小

首先需对整体 bundle 进行分析,明确其组成及各部分所占大小。

推荐使用 webpack-bundle-analyzer。安装后在 webpack.dev.config.js 中添加插件,每次启动后自动在浏览器打开分析结果,如图所示。


plugins.push( new BundleAnalyzerPlugin());

React 代码优化策略

此外,还可以将打包过程输出为 json 文件。


webpack --profile --json -> stats.json

然后访问以下两个网站进行分析:

  1. webpack/analyse
  2. Webpack Chart

通过上述图表分析,可清晰看到整个 bundle.js 的组成部分及对应大小。

解决 bundle.js 体积过大的思路如下:

  1. 生产环境启用压缩等插件,去除不必要插件
  2. 拆分业务代码与第三方库及公共模块
  3. webpack 开启 gzip 压缩
  4. 按需加载

生产环境启用压缩等插件,去除不必要插件

确保在生产环境中启动 webpack.DefinePlugin 和 webpack.optimize.UglifyJsPlugin。


const plugins = [
 new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
 }),
  new webpack.optimize.UglifyJsPlugin({
  compress: {
   warnings: false,
   drop_console: false //eslint-disable-line
  }
  })   
]

拆分业务代码与第三方库及公共模块

由于项目业务代码变更频率高,而第三方库代码变化相对较少。若将业务代码与第三方库打包至同一 chunk,每次构建时即使只修改一行业务代码且第三方库未变,也会导致整个 chunk 的 hash 与上次不同。这并非期望结果。我们希望第三方库代码不变时,其对应 hash 也保持不变,从而利用浏览器缓存,提升页面加载性能并缩短加载时间。

因此,可将第三方库代码单独拆分为 vendor chunk,与业务代码分离。这样即使业务代码频繁变化,只要第三方库代码未变,对应 hash 即保持不变。

首先在 entry 配置中设置 app 和 vendor 两个 chunk。


entry: {
 vendor: [path.join(__dirname, 'dll', 'vendors.js')],
 app: [path.join(__dirname, 'src/index')]
},
output: {
 path: path.resolve(__dirname, 'build'),
 filename: '[name].[chunkhash:8].js'
},

其中 vendors.js 为自定义定义的需纳入 vendor 的第三方库列表,如下:


require('babel-polyfill');
require('classnames');
require('intl');
require('isomorphic-fetch');
require('react');
require('react-dom');
require('immutable');
require('redux');

然后通过 CommonsChunkPlugin 拆分第三方库。


plugins.push(
 // 拆分第三方库
 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }),
 // 拆分 webpack 自身代码
 new webpack.optimize.CommonsChunkPlugin({
  name: 'runtime',
  minChunks: Infinity
 })
);

上述配置有两个细节需要注意:

  1. 使用 chunkhash 而不用 hash
  2. 单独拆分 webpack 自身代码

使用 chunkhash 而不用 hash

先来看看这二者的区别:

  1. hash 是 build-specific,任何文件改动都会导致编译结果不同,适用于开发阶段。
  2. chunkhash 是 chunk-specific,根据每个 chunk 内容计算出的 hash,适用于生产环境。

因此,为保证第三方库不变时 vendor.js 的 hash 也保持不变,我们在 output.filename 中采用了 chunkhash。

单独拆分 webpack 自身代码

Webpack 存在一个已知问题:

webpack 自身的 boilerplate 和 manifest 代码可能在每次编译时都会变化。

这导致仅在入口文件修改一行代码,编译出的 vendor 和 entry chunk 均发生变化,因为它们自身都包含这部分代码。

这并不合理,因为实际上第三方库代码未变,vendor 不应随业务代码变化而变化。

因此需要将 webpack 这部分代码分离抽离。


new webpack.optimize.CommonsChunkPlugin({
   name: "runtime",
   minChunks: Infinity
}),

其中的 name 只要不在 entry 中即可,通常使用 "runtime" 或 "manifest"。

另一个参数 minChunks 表示:在传入公共 chunk(commons chunk) 之前所需要包含的最少数量的 chunks。数量必须大于等于 2,或者少于等于 chunks 的数量,传入 Infinity 会立即生成公共 chunk,但里面没有模块。

更多关于 CommonChunkPlugin 的信息可查看官方文档。

拆分公共资源

与上述拆分第三方库类似,拆分公共资源可将公用模块单独打出一个 chunk。可设置 minChunk 来选择共用多少次模块才将它们抽离。配置如下:


new webpack.optimize.CommonsChunkPlugin({
 name: 'common',
 minChunks: 2,
}),

是否需要进行这一步优化可自行根据项目的业务复用度来判断。

开启 gzip

使用 CompressionPlugin 插件开启 gzip 即可:


// 添加 gzip
new CompressionPlugin({
 asset: '[path].gz[query]',
 algorithm: 'gzip',
 test: /\.(js|html)$/,
 threshold: 10240,
 minRatio: 0.8
})

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

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章