代码分割
代码分割是优化应用性能的重要手段。本章介绍为什么需要代码分割,以及如何通过 Entry 分割、SplitChunks 和动态导入实现代码分割。
📋 学习目标
- ✅ 理解为什么需要代码分割
- ✅ 掌握 Entry 分割
- ✅ 掌握 SplitChunks 配置
- ✅ 学会使用动态导入
- ✅ 理解懒加载和预加载
为什么需要代码分割
问题
将所有代码打包到一个文件中会导致:
- 文件体积过大
- 首次加载时间长
- 无法利用浏览器缓存
- 用户体验差
解决方案
代码分割可以将代码拆分成多个小文件:
- 按需加载
- 并行加载
- 利用浏览器缓存
- 提升加载速度
Entry 分割
基础分割
module.exports = {
entry: {
main: './src/index.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name].bundle.js'
}
}分离第三方库
module.exports = {
entry: {
main: './src/index.js',
vendor: ['react', 'react-dom', 'lodash']
}
}问题:手动维护,容易出错
SplitChunks 自动分割
基础配置
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}完整配置
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 'initial' | 'async' | 'all'
minSize: 20000, // 最小 chunk 大小(字节)
maxSize: 0, // 最大 chunk 大小
minChunks: 1, // 最小引用次数
maxAsyncRequests: 30, // 最大异步请求数
maxInitialRequests: 30, // 最大初始请求数
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: -10,
reuseExistingChunk: true
}
}
}
}
}chunks 选项
'initial':只分割初始 chunk'async':只分割异步 chunk(动态导入)'all':分割所有 chunk(推荐)
cacheGroups 配置
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
// 默认组
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
},
// 第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
chunks: 'all'
},
// 公共代码
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
},
// React 相关
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20,
chunks: 'all'
}
}
}
}
}动态导入(Dynamic Import)
基础用法
// 动态导入
import('./module').then(module => {
module.doSomething()
})React 懒加载
import React, { lazy, Suspense } from 'react'
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
)
}Vue 懒加载
const routes = [
{
path: '/home',
component: () => import('./pages/Home.vue')
},
{
path: '/about',
component: () => import('./pages/About.vue')
}
]预加载和预获取
预加载(Preload)
// 高优先级,立即加载
import(/* webpackPreload: true */ './module')预获取(Prefetch)
// 低优先级,空闲时加载
import(/* webpackPrefetch: true */ './module')使用场景
预加载:关键资源,需要立即使用
// 用户点击按钮后立即需要
button.onclick = () => {
import(/* webpackPreload: true */ './dialog')
}预获取:可能需要的资源
// 路由懒加载,预获取下一个页面
const Home = lazy(() => import(/* webpackPrefetch: true */ './pages/Home'))代码分割策略
策略一:按框架分离
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
framework: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router)[\\/]/,
name: 'framework',
priority: 40,
chunks: 'all'
}
}
}
}
}策略二:按功能分离
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
utils: {
test: /[\\/]src[\\/]utils[\\/]/,
name: 'utils',
priority: 10,
chunks: 'all'
},
components: {
test: /[\\/]src[\\/]components[\\/]/,
name: 'components',
priority: 10,
chunks: 'all'
}
}
}
}
}策略三:按页面分离
// 使用动态导入
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Contact = lazy(() => import('./pages/Contact'))完整配置示例
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000,
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: -10,
chunks: 'all'
},
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
},
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20,
chunks: 'all'
}
}
},
runtimeChunk: {
name: 'runtime'
}
}
}最佳实践
1. 分离运行时代码
module.exports = {
optimization: {
runtimeChunk: 'single' // 或 { name: 'runtime' }
}
}2. 合理设置 chunk 大小
module.exports = {
optimization: {
splitChunks: {
minSize: 20000,
maxSize: 244000 // 避免 chunk 过大
}
}
}3. 使用动态导入
// ✅ 推荐:按需加载
const Component = lazy(() => import('./Component'))
// ❌ 不推荐:一次性加载所有
import Component from './Component'4. 配置预获取
// 预获取可能访问的页面
const NextPage = lazy(() =>
import(/* webpackPrefetch: true */ './pages/NextPage')
)总结
- Entry 分割:手动分离入口
- SplitChunks:自动分割代码
- 动态导入:按需加载模块
- 预加载/预获取:优化加载策略
- 最佳实践:分离运行时代码,合理设置大小
下一步
- 性能优化 - 学习性能优化技巧
- 实战项目:从零搭建 React 项目 - 实践代码分割