Hi,我是前端人类学!
在互联网流量日益昂贵的今天,一个前端项目的包体积大小直接关系到用户体验——每多加载1MB资源,用户可能就要多等待数秒。如果你的项目打包后体积膨胀,加载速度缓慢,本文将带你从四个维度入手,系统性地为前端包体积“瘦身”。
文章目录
- 一、代码分割:从“大块头”到“按需加载”
- 1.1 多入口拆分
- 1.2 SplitChunks 提取公共依赖
- 1.3 动态导入实现按需加载
- 二、图片压缩:视觉无损,体积减半
- 2.1 格式升级:WebP 和 AVIF
- 2.2 结合 `<picture>` 标签渐进增强
- 2.3 构建工具中的图片压缩
- 三、移除冗余依赖:给 node_modules 做“大扫除”
- 3.1 扫描未使用的依赖
- 3.2 安全清理
- 3.3 依赖瘦身技巧
- 四、Externals:把大象搬出房间
- 4.1 基本用法
- 4.2 多模块规范支持
- 4.3 实际效果
一、代码分割:从“大块头”到“按需加载”
代码分割是包体积优化的核心手段之一。它的核心理念是:不让用户一次性加载所有代码,而是按需加载。就像去餐厅吃饭,不会把所有菜一次性端上来,而是现点现做。
1.1 多入口拆分
最基础的代码分割方式是配置多入口(entry points)。但这种方式有一个明显的缺陷——如果多个入口共用相同的依赖(比如lodash),这些依赖会被重复打包进每个入口文件中。
1.2 SplitChunks 提取公共依赖
更优雅的方式是使用 Webpack 的SplitChunksPlugin,它能够自动提取公共依赖,避免重复打包:
// webpack.config.jsmodule.exports={optimization:{splitChunks:{chunks:'all',cacheGroups:{vendors:{test:/[\\/]node_modules[\\/]/,name:'vendors',priority:-10,reuseExistingChunk:true}}}}};配置splitChunks后,项目中的第三方依赖(如 React、Vue、lodash 等)会被抽离成独立的 chunk,浏览器可以缓存这部分代码,用户在页面间切换时无需重复下载。
1.3 动态导入实现按需加载
对于非首屏必需的模块,使用动态import()语法可以实现真正的“用到时才加载”:
// 路由懒加载示例constAdminPage=()=>import(/* webpackChunkName: "admin" */'./pages/AdminPage');// 条件加载示例if(process.env.FEATURE_FLAG==='special'){constmodule=awaitimport(/* webpackChunkName: "special" */'./SpecialComponent');// 使用 module}通过/* webpackChunkName: "special" */魔法注释,还可以自定义生成 chunk 的名称,便于后续分析和缓存管理。
二、图片压缩:视觉无损,体积减半
图片往往是前端项目中体积占比最大的资源类型。优化图片,往往能获得立竿见影的效果。
2.1 格式升级:WebP 和 AVIF
现代浏览器广泛支持的 WebP 和 AVIF 格式,在同等画质下体积远小于传统的 JPEG 和 PNG。将图片转为 WebP 或 AVIF 格式,通常可以减少25%~50%的体积。
可以使用sharp等工具进行批量转换:
npx lake-cimg ./assets-r-q80--out-dir ./dist以上命令会将assets目录下的图片递归压缩并转为 WebP 格式,质量设为 80。
2.2 结合<picture>标签渐进增强
为了保证兼容性,可以使用<picture>标签为现代浏览器提供 AVIF/WebP,为老旧浏览器提供 JPEG 兜底:
<picture><sourcesrcset="hero.avif"type="image/avif"><sourcesrcset="hero.webp"type="image/webp"><imgsrc="hero.jpg"alt="头图"width="1200"height="600"></picture>2.3 构建工具中的图片压缩
在 Webpack 构建流程中,可以配合image-webpack-loader或配置url-loader的limit阈值,将小图片转为 Base64 内联以减少 HTTP 请求。
三、移除冗余依赖:给 node_modules 做“大扫除”
项目运行一段时间后,package.json中往往会累积大量不再使用的依赖。它们不仅占用磁盘空间,还会拖慢安装和构建速度。
3.1 扫描未使用的依赖
使用 AST 扫描工具可以精准检测哪些依赖已被引入,哪些是“僵尸依赖”:
npx unused-dep-cleaner scan# 或npx dependency-optimizer scan这些工具会分析项目源码中的import、require和动态import(),与package.json中的依赖列表进行比对,准确识别未使用的包。
3.2 安全清理
确认无误后,可以执行清理命令移除冗余依赖:
npx unused-dep-cleaner clean --dry-run# 先预览要删除的内容npx unused-dep-cleaner clean# 正式执行清理清理完成后,建议重新执行npm install更新package-lock.json。
3.3 依赖瘦身技巧
除了移除未使用的依赖,还可以考虑替换体积更大的同类库。例如,用dayjs替换moment可以节省大量空间;使用lodash-es配合 Tree Shaking,可以实现按需引入而不是全量打包。
四、Externals:把大象搬出房间
externals配置的作用是告诉 Webpack:某些依赖不要打包进最终产物,它们会由外部环境(如 CDN)提供。这就像搬家时发现沙发太大搬不进电梯——直接拆了重买,而不是把整栋楼拆了。
4.1 基本用法
以使用 CDN 加载 jQuery 为例,先配置externals:
// webpack.config.jsmodule.exports={externals:{jquery:'jQuery'// 键是包名,值是全局变量名}};然后在 HTML 中通过 CDN 引入 jQuery:
<scriptsrc="https://code.jquery.com/jquery-3.6.0.min.js"></script>代码中的import $ from 'jquery'依然可以正常工作,但 jQuery 的代码不会再被打包进 bundle。
4.2 多模块规范支持
对于 UMD 格式的库,可以配置多种模块规范下的对应关系:
externals:{lodash:{commonjs:'lodash',amd:'lodash',root:'_'// 全局变量名}}这样打包出来的库既能在 Node.js(CommonJS)中使用,也能在浏览器(AMD/全局变量)中使用。
4.3 实际效果
在实际项目中,使用externals提取 Vue、Element UI、ECharts 等重量级依赖后,打包体积可减少 50% 以上,同时打包速度也有显著提升。以 Vue 项目为例,仅将vue、vuex、vue-router等核心库外部化,chunk-vendors.js的体积就能从 MB 级降至 KB 级。
优化不应盲目进行。建议先使用
webpack-bundle-analyzer等工具分析打包产物,找出真正的“体积大户”,再针对性地采取优化手段。优化的目标是改善用户体验,而不是追求数字上的极致——找到适合你项目的平衡点,才是最优解。