如何在 Next js 中减少 Bundle 大小
Source: Dev.to
为什么包大小很重要?
一个庞大的 bundle 会在用户开始交互之前就让你的应用变慢。
- 首次加载更慢: 浏览器需要更长时间来下载和解析 JavaScript。
- 核心网页指标受影响: Google 重视性能,这会影响 SEO 排名。
- 移动端体验差: 在慢速网络下,大体积的 bundle 会让人感到痛苦。
削减 bundle 大小直接关系到用户满意度和搜索排名。
第一步:分析你的 Bundle
你不能盲目优化。首先,了解你的 bundle 中有什么。
使用 @next/bundle-analyzer:
npm install @next/bundle-analyzer
更新 next.config.js:
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({});
运行分析器:
ANALYZE=true npm run build
将生成你的 bundle 的树形图。
尝试 source-map-explorer —— 另一个可视化展示哪些文件对 bundle 大小贡献最大的工具。
目标是在进行任何修复之前,了解最大的体积罪魁祸首。
第2步:更聪明地导入
一个常见的错误是导入整个库,而实际上只需要一个或两个函数。
错误示例:
import _ from 'lodash';
const result = _.isEmpty(obj);
更好的方式:
import isEmpty from 'lodash/isEmpty';
const result = isEmpty(obj);
第二种方法只包含所需的内容。Next.js 还支持 tree‑shaking 以及在 next.config.js 中的 optimizePackageImports 选项,以自动剔除库中未使用的部分。
第三步:使用动态导入
并非所有代码都需要在页面首次渲染时加载。如果只在交互后使用,则可以稍后再加载。
动态组件导入:
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('../components/Chart'), {
ssr: false,
loading: () => <p>Loading...</p>,
});
这会将图表代码排除在初始 bundle 之外。动态导入非常适用于:
- 模态框
- 图表 / 图形
- 第三方小部件
- 很少访问的页面或区域
第4步:将繁重代码保留在服务器上
Next.js 将服务器端代码和客户端代码分离。请明智地利用这种分离。
- 在
getServerSideProps或getStaticProps中进行数据获取,而不是在客户端获取所有数据。 - 使用 Server Components(App Router)来处理不需要在浏览器中运行的逻辑。
- 避免将不必要的业务逻辑发送到客户端。
这样可以减小 bundle 大小,提升安全性,并提高性能。
第5步:外部化或替换大型依赖
一些库实在是太大了。你有两种选择:按需懒加载它们或直接替换。
- Charts: 使用更轻量的图表库,或按需加载大型库。
- Date libraries: 用 Day.js 或 date‑fns 替代 Moment.js。
- React → Preact: 有些项目将 React 换成 Preact 以获得更小的包(先检查兼容性)。
在 next.config.js 中,你也可以将仅服务器端使用的包外部化,这样它们就不会进入客户端 bundle。
Step 6: Optimize Assets (Not Just JavaScript)
优化图像、字体和 CSS 可提升整体性能。
- Images: 使用 Next.js
<Image>组件实现懒加载、压缩和响应式尺寸。 - Fonts: 仅导入所需的字符和字重;考虑自行托管 Google Fonts。
- CSS: 启用 Tailwind 的 purge 功能或其他 CSS tree‑shaking,以移除未使用的样式。
步骤 7:持续监控
一次优化并不足够。每个新引入的依赖或功能都可能增加体积。
- 设定预算: 使用
size-limit等工具,当 bundle 超过设定大小时会给出警告。 - 重新运行分析器: 将其作为 CI 工作流的一部分,在部署前执行。
- 跟踪性能: 监控 Lighthouse 分数、LCP(Largest Contentful Paint)和 TTI(Time To Interactive)。
快速检查清单
- 使用
@next/bundle-analyzer进行分析。 - 用更轻量的替代品替换大型依赖。
- 仅导入所需的内容。
- 对非关键 UI 使用
next/dynamic。 - 将繁重的逻辑放在服务器端。
- 优化图片、字体和 CSS。
- 持续监控 bundle 大小。
最后思考
在 Next.js 中减少 bundle 大小并不是要对每一个字节都斤斤计较;而是要关注真正重要的方面:更快的加载时间、更好的 SEO,以及更满意的用户。先从大幅度的收益入手——懒加载组件和裁剪依赖——然后通过导入优化和资源处理进行微调。一旦实施这些实践,你会明显感受到应用的流畅度和响应速度的提升。