魯斯前端布魯斯前端

文章中英模式

常見的前端面試題目 - 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. 1. 加載失敗處理:使用 try/catch 或 Promise 的 catch 方法處理載入錯誤
  2. 2. 加載狀態:使用 React 的 Suspense 或自定義載入指示器
  3. 3. 重複載入:使用 Webpack 的 SplitChunksPlugin 提取共用代碼
  4. 4. 緩存問題:配置合適的緩存策略和文件名哈希
  5. 5. 預加載關鍵模組:使用 <link rel="preload"> 預先載入重要資源