文章中英模式
常見的前端面試題目 - 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">預先載入重要資源