文章中英模式
常见的前端面试题目 - Code Splitting 与 Dynamic Import 完全解析
深入理解前端优化技术中的代码分割和动态导入,包含实作方式、效能比较,以及面试常见问题的完整解答。
文章中英模式

懒得看文章?那就来看视频吧
基本概念
Code Splitting(代码分割)是一种优化前端应用程序效能的技术,它允许将JavaScript包(bundle)分割成更小的块(chunks),并在需要时才进行载入,而不是一次性载入所有代码。
Dynamic Import(动态导入)是实现代码分割的主要方法,它允许程序在运行时动态载入模块,而不是在应用程序启动时就载入所有模块。
// 传统方式 - 一次性载入
import { heavyFunction } from './heavyModule';
// 动态导入 - 按需载入
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavyModule');
heavyFunction();
});为什么需要 Code Splitting?
问题情境
想象一个电商网站,有首页、商品列表、购物车、结账和管理后台等页面。在没有代码分割的情况下:
- 1. 用户访问首页时会下载整个应用程序的JS代码
- 2. 包括他们可能永远不会访问的页面(如管理后台)
- 3. 初次载入时间过长,影响用户体验
效益对比
| 不使用 Code Splitting | 使用 Code Splitting |
|---|---|
| 单一大型JS包 (2-5MB+) | 多个小型JS包 (200-500KB) |
| 初始载入时间长 | 初始载入时间短 |
| 页面显示延迟 | 页面快速显示 |
| 流量消耗大 | 按需载入,流量优化 |
实现方式
1. 基于路由的代码分割
// React Router + React.lazy 实现
import { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// 懒加载各个页面组件
const Home = lazy(() => import('./pages/Home'));
const Shop = lazy(() => import('./pages/Shop'));
const Cart = lazy(() => import('./pages/Cart'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/shop" element={<Shop />} />
<Route path="/cart" element={<Cart />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}2. 基于组件的代码分割
import { lazy, Suspense, useState } from 'react';
// 懒加载大型组件
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
显示图表
</button>
{showChart && (
<Suspense fallback={<div>图表加载中...</div>}>
<HeavyChart />
</Suspense>
)}
</div>
);
}Dynamic Import 原理
Dynamic Import 是 ECMAScript 的一个特性,使用 import() 函数来动态载入模块:
- 1. 返回一个 Promise 对象
- 2. 异步载入指定的模块
- 3. 仅在需要时才执行载入,不阻塞主线程
- 4. 支持条件式载入
// 基于条件动态载入
function loadModule(condition) {
if (condition) {
import('./moduleA.js')
.then(module => {
module.doSomething();
})
.catch(error => {
console.error('模块载入失败:', error);
});
} else {
import('./moduleB.js')
.then(module => {
module.doSomethingElse();
});
}
}Dynamic Import 与 Lazy Loading 的关系
- 1. Dynamic Import 是语言层面的特性,提供按需载入模块的能力
- 2. Lazy Loading 是一种设计模式,利用 Dynamic Import 来实现延迟载入
- 3. Lazy Loading 不仅可用于代码分割,还可用于图片、视频等资源
🔥 常见面试题目
(一)Code Splitting 和 Dynamic Import 有什么区别?
解答:Code Splitting 是一种优化技术,目的是将代码分割成多个小块。而 Dynamic Import 是实现 Code Splitting 的一种方法,它使用 import() 语法在运行时动态载入模块。两者关系是:Code Splitting 是目标,Dynamic Import 是工具。
Next.js 15 Bundle 大小比较:
【不使用 Code Splitting】
┌───────────────────────────────────────────────────────┐
│ │
│ 主要 Bundle (1.2MB) │
│ ┌─────────────────┬─────────────────┬───────────────┐│
│ │ 页面基本代码 │ 图表库 (600KB) │ 其他依赖 ││
│ │ (200KB) │ 包含 D3.js │ (400KB) ││
│ └─────────────────┴─────────────────┴───────────────┘│
│ │
└───────────────────────────────────────────────────────┘
初始载入: 1.2MB (即使用户不查看图表也要下载全部内容)
【使用 Code Splitting】
┌───────────────────────────────┐ ┌───────────────────┐
│ │ │ │
│ 主要 Bundle (600KB) │ │ 图表 Chunk │
│ ┌─────────────┬─────────────┐│ │ (600KB) │
│ │ 页面基本代码 │ 其他依赖 ││ │ - 图表库 │
│ │ (200KB) │ (400KB) ││ │ - D3.js │
│ └─────────────┴─────────────┘│ │ - 相关依赖 │
│ │ │ │
└───────────────────────────────┘ └───────────────────┘
初始载入: 600KB 按需载入: +600KB
(减少 50% 初始载入大小)(二)什么情况下应该使用 Code Splitting?
解答:以下情况特别适合使用代码分割:
| 应用场景 | 为何需要代码分割 |
|---|---|
| 大型单页应用(SPA) | SPA一次性载入所有JS会导致初始载入时间过长 |
| 包含大量第三方库的应用 | 第三方库通常体积较大,分割后可按需载入 |
| 具有复杂后台管理功能的网站 | 普通用户不需要载入管理功能相关代码 |
| 包含不常用但体积大的功能 | 图表、编辑器等功能只在特定场景使用,不需要一开始就载入 |
| 需要优化首屏载入时间的应用 | 分割后可优先载入关键路径代码,提升首屏速度 |
(三)实现 Code Splitting 时可能遇到的挑战及解决方案?
解答:
- 1. 加载失败处理:使用 try/catch 或 Promise 的 catch 方法处理载入错误
- 2. 加载状态:使用 React 的 Suspense 或自定义载入指示器
- 3. 重复载入:使用 Webpack 的 SplitChunksPlugin 提取共用代码
- 4. 缓存问题:配置合适的缓存策略和文件名哈希
- 5. 预加载关键模块:使用
<link rel="preload">预先载入重要资源