React项目如何减小bundle.js/main.[hash].js主包体积?
未eject场景下React生产构建main包体积优化方案(已实现路由懒加载)
以下方案均不需要执行eject操作,可直接落地:
- 第一步先做包体分析,定位冗余来源
执行npx react-scripts build --stats生成构建统计文件,排查main包中实际包含的模块。注意Lighthouse提示的「未使用JavaScript」指首屏加载阶段未执行的代码,并非你写了冗余死代码——哪怕业务代码无冗余,第三方依赖全量引入、首屏加载了非必要逻辑都会触发该提示。 - 无需eject调整构建配置
可以用@craco/craco或者react-app-rewired覆盖默认webpack配置,不需要执行eject暴露配置文件:- 安装依赖后将package.json中scripts的
react-scripts命令替换为对应工具的命令 - 配置splitChunks拆包规则,将React运行时、通用工具库等多chunk共用的依赖从main包中拆分出来,示例配置:
// craco.config.js module.exports = { webpack: { configure: (config) => { config.optimization.splitChunks = { chunks: 'all', cacheGroups: { reactCore: { test: /[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types)[\\/]/, name: 'react-core', priority: 40, }, commonUtils: { test: /[\\/]node_modules[\\/](axios|dayjs|lodash-es)[\\/]/, name: 'common-utils', priority: 30, }, default: { minChunks: 2, priority: 20, reuseExistingChunk: true, } } } config.mode = 'production' return config } } }- 在package.json中添加
sideEffects配置,标记无副作用的文件供构建时做tree shaking,注意要把全局样式文件加入例外列表避免被误删:
"sideEffects": ["*.css", "*.scss", "*.less"] - 安装依赖后将package.json中scripts的
- 替换重型依赖、实现按需引入
这部分优化收益最高:- 用轻量依赖替换重型依赖:比如用dayjs替换moment(体积减少70%+,API兼容),用lodash-es替代全量lodash,导入工具方法时直接引入对应文件路径,比如
import debounce from 'lodash-es/debounce',不要写import { debounce } from 'lodash' - UI组件库配置按需引入:如果使用AntD、Mantine等组件库,不要全量引入组件和样式,通过babel插件实现按需导入,仅打包实际用到的组件代码
- 精简browserslist配置:如果不需要兼容IE、过低版本浏览器,调整package.json中的browserslist生产环境配置,移除不必要的polyfill,示例配置:
"browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all", "not ie <= 11" ] } - 用轻量依赖替换重型依赖:比如用dayjs替换moment(体积减少70%+,API兼容),用lodash-es替代全量lodash,导入工具方法时直接引入对应文件路径,比如
- 进一步精简main包首屏加载内容
路由级懒加载之外,还可以做更细粒度的代码分割:- 对首屏不可见的组件做懒加载:比如弹窗、抽屉、非默认激活的标签页内容、页脚区域的非核心组件,全部用
React.lazy配合Suspense包裹,不要在首屏组件中同步引入 - 延迟加载非首屏必须的逻辑:埋点SDK、客服插件、全局权限/主题的非首屏初始化逻辑,放到
requestIdleCallback或者首屏内容挂载完成后的回调里动态import,不要在App根组件顶层同步引入 - 精简根组件/全局Context逻辑:不要把大量业务逻辑、配置代码堆在App根组件或者全局Provider中,这部分代码默认会被打入main包,非首屏必须的逻辑抽离后动态加载
- 对首屏不可见的组件做懒加载:比如弹窗、抽屉、非默认激活的标签页内容、页脚区域的非核心组件,全部用
- 开启更高压缩率的传输压缩
不要只看构建产物的源文件体积,实际网络传输的是压缩后的文件:- 构建时可以配置brotli压缩,压缩率比gzip高20%左右,138KB的源文件经brotli压缩后仅40-50KB
- 如果自己托管服务,在Nginx/CDN层开启brotli压缩即可,不需要在构建阶段生成压缩文件
- 生产环境不需要sourcemap的话,设置环境变量
GENERATE_SOURCEMAP=false,可以进一步减小产物体积
按以上步骤优化后,main包的gzip/brotli传输体积通常可以控制在50KB以内,不会再触发Lighthouse的未使用JavaScript告警。
内容的提问来源于stack exchange,提问作者Yuvraj Chaudhari
相关产品推荐
相关产品推荐

