如何分析并优化 Bundle 大小?
Bundle 分析工具
1. Next.js 内置分析
# 安装分析工具
npm install --save-dev @next/bundle-analyzer
# 或使用 yarn
yarn add -D @next/bundle-analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
// 其他配置
})
# 运行分析
ANALYZE=true npm run build
# 或
npm run build && npm run analyze
2. Webpack Bundle Analyzer
// next.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
webpack: (config, { isServer }) => {
if (process.env.ANALYZE === 'true') {
config.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'server',
openAnalyzer: true,
})
)
}
return config
},
}
Bundle 大小优化策略
1. 代码分割 (Code Splitting)
// 动态导入组件
import dynamic from 'next/dynamic'
// 懒加载组件
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <p>Loading...</p>,
ssr: false, // 禁用服务端渲染
})
// 条件加载
const AdminPanel = dynamic(() => import('./AdminPanel'), {
loading: () => <p>Loading admin panel...</p>,
})
function MyPage({ user }) {
return (
<div>
<h1>Main Content</h1>
<HeavyComponent />
{user.isAdmin && <AdminPanel />}
</div>
)
}
2. 路由级别代码分割
// app/dashboard/page.js - 自动代码分割
export default function Dashboard() {
return <div>Dashboard Content</div>
}
// app/admin/page.js - 自动代码分割
export default function Admin() {
return <div>Admin Content</div>
}
3. 第三方库优化
// 按需导入
import { debounce } from 'lodash/debounce'
// 而不是
import _ from 'lodash'
// 使用更轻量的替代品
import { format } from 'date-fns'
// 而不是
import moment from 'moment'
// 动态导入大型库
const Chart = dynamic(() => import('react-chartjs-2'), {
loading: () => <div>Loading chart...</div>,
})
4. Tree Shaking 优化
// 使用 ES6 模块语法
import { specificFunction } from 'large-library'
// 避免默认导入
import * as utils from 'utils' // ❌ 导入整个模块
import { specificUtil } from 'utils' // ✅ 只导入需要的
// 配置 package.json
{
"sideEffects": false, // 启用 tree shaking
"module": "esm/index.js", // ES 模块入口
"main": "cjs/index.js" // CommonJS 入口
}
实际优化案例
1. 电商网站优化
// 优化前:所有组件都在主 bundle 中
import ProductList from './ProductList'
import ProductDetail from './ProductDetail'
import ShoppingCart from './ShoppingCart'
import Checkout from './Checkout'
// 优化后:按需加载
const ProductList = dynamic(() => import('./ProductList'))
const ProductDetail = dynamic(() => import('./ProductDetail'))
const ShoppingCart = dynamic(() => import('./ShoppingCart'))
const Checkout = dynamic(() => import('./Checkout'))
function EcommerceApp() {
const [currentPage, setCurrentPage] = useState('list')
return (
<div>
{currentPage === 'list' && <ProductList />}
{currentPage === 'detail' && <ProductDetail />}
{currentPage === 'cart' && <ShoppingCart />}
{currentPage === 'checkout' && <Checkout />}
</div>
)
}
2. 博客网站优化
// 优化前:所有功能都在主 bundle 中
import CodeHighlighter from './CodeHighlighter'
import ImageGallery from './ImageGallery'
import CommentSystem from './CommentSystem'
// 优化后:按需加载
const CodeHighlighter = dynamic(() => import('./CodeHighlighter'), {
loading: () => <div>Loading code highlighter...</div>,
})
const ImageGallery = dynamic(() => import('./ImageGallery'), {
loading: () => <div>Loading gallery...</div>,
})
const CommentSystem = dynamic(() => import('./CommentSystem'), {
loading: () => <div>Loading comments...</div>,
})
function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
{post.hasCode && <CodeHighlighter code={post.code} />}
{post.hasImages && <ImageGallery images={post.images} />}
{post.allowComments && <CommentSystem postId={post.id} />}
</article>
)
}
高级优化技巧
1. 预加载关键资源
// 预加载关键路由
import Link from 'next/link'
function Navigation() {
return (
<nav>
<Link href="/dashboard" prefetch={true}>
Dashboard
</Link>
<Link href="/profile" prefetch={true}>
Profile
</Link>
</nav>
)
}
// 预加载关键组件
const CriticalComponent = dynamic(() => import('./CriticalComponent'), {
loading: () => <div>Loading...</div>,
})
// 在页面加载时预加载
useEffect(() => {
import('./CriticalComponent')
}, [])
2. 共享依赖优化
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
// 优化共享依赖
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true,
},
},
}
return config
},
}
3. 图片和字体优化
// 使用 next/image 优化图片
import Image from 'next/image'
function OptimizedImage({ src, alt }) {
return (
<Image
src={src}
alt={alt}
width={800}
height={600}
priority={false} // 非关键图片不优先加载
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
)
}
// 使用 next/font 优化字体
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
preload: true,
})
监控和分析
1. 构建时分析
# 分析构建输出
npm run build
# 查看构建统计
npm run build -- --profile
# 分析 bundle 大小
ANALYZE=true npm run build
2. 运行时监控
// 监控 bundle 加载性能
function PerformanceMonitor() {
useEffect(() => {
// 监控资源加载时间
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'resource') {
console.log(`${entry.name}: ${entry.duration}ms`)
}
})
})
observer.observe({ entryTypes: ['resource'] })
return () => observer.disconnect()
}, [])
return null
}
3. 持续监控
// 设置性能预算
// next.config.js
module.exports = {
experimental: {
webpackBuildWorker: true,
},
webpack: (config, { isServer }) => {
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
maxSize: 244000, // 设置最大 chunk 大小
}
}
return config
},
}
最佳实践总结
1. 代码分割策略
// 按路由分割
const Dashboard = dynamic(() => import('./Dashboard'))
const Profile = dynamic(() => import('./Profile'))
// 按功能分割
const Chart = dynamic(() => import('./Chart'))
const Table = dynamic(() => import('./Table'))
// 按用户权限分割
const AdminPanel = dynamic(() => import('./AdminPanel'))
2. 依赖优化
// 使用轻量级替代品
import { format } from 'date-fns' // 而不是 moment
import { debounce } from 'lodash/debounce' // 而不是整个 lodash
// 按需导入
import { Button } from 'antd' // 而不是整个 antd
3. 资源优化
// 图片优化
<Image src="/image.jpg" width={800} height={600} />
// 字体优化
const font = Inter({ display: 'swap' })
// 脚本优化
<Script src="/script.js" strategy="afterInteractive" />
总结
Bundle 大小优化策略:
分析工具:
- Next.js 内置分析器
- Webpack Bundle Analyzer
- 构建时和运行时监控
优化策略:
- 代码分割和动态导入
- Tree shaking 和按需导入
- 第三方库优化
- 共享依赖优化
最佳实践:
- 按路由和功能分割代码
- 使用轻量级替代品
- 预加载关键资源
- 设置性能预算
- 持续监控和优化