[Lv1] 路由層級優化:三層 Lazy Loading
透過三層路由 Lazy Loading,將首次載入從 12.5MB 降至 850KB,首屏時間縮短 70%。
問題背景(Situation)
專案特性:
- 📦 27+ 個不同的多租戶版型(多租戶架構)
- 📄 每個版型有 20-30 個頁面(首頁、大廳、促銷、代理、新聞等)
- 💾 如果一次載入所有程式碼:首次進入需要下載 10MB+ 的 JS 檔案
- ⏱️ 使用者等待時間超過 10 秒(尤其在手機網路環境下)
優化目標(Task)
- 減少首次載入的 JavaScript 體積(目標:< 1MB)
- 縮短首屏時間(目標:< 3 秒)
- 按需載入(使用者只下載需要的內容)
- 維持開發體驗(不能影響開發效率)
解決方案(Action)
我們採用了三層路由 Lazy Loading 的策略,從「版型」→「頁面」→「權限」三個層級進行優化。
第 1 層:動態模板載入
// src/router/routes.ts
export default async function (siteKey?: string) {
// 根據環境變數動態載入對應的版型路由
const module = await import(`../../template/${siteKey}/router/routes.ts`);
return { routes: module.routes };
}
說明:
- 專案有 27 個版型,但用戶只會使用其中 1 個
- 透過 environment.json 判斷當前是哪個品牌
- 只載入該品牌的路由配置,其他 26 個版型完全不載入
效果:
- 首次載入減少約 85% 的路由配置程式碼
第 2 層:頁面 Lazy Loading
// 傳統寫法(X - 不好)
import HomePage from './pages/HomePage.vue';
component: HomePage; // 所有頁面都會被打包進 main.js
// 我們的寫法(✓ - 好)
component: () => import('app/template/okbet_green/pages/HomePage/Home.vue');
- 每個路由使用 箭頭函式 + import() 包裹
- 只有當用戶真正訪問該頁面時,才會下載對應的 JS chunk
- Vite 會自動將每個頁面打包成獨立的檔案
第 3 層:按需載入策略
// src/router/index.ts
router.beforeEach((to, from, next) => {
const { needAuth } = to.meta;
if (needAuth && !store.isLogin) {
// 未登入用戶不會載入「代理中心」等需要登入的頁面
return next({ name: 'HomePage' });
}
next();
});
✅ 優化成效(Result)
優化前:
首次載入:main.js (12.5 MB)
首屏時間:8-12 秒
包含所有 27 個版型 + 所有頁面
優化後:
首次載入:main.js (850 KB) ↓ 93%
首屏時間:1.5-2.5 秒 ↑ 70%
僅包含核心程式碼 + 當前首頁
具體數據:
- ✅ JavaScript 體積減少:12.5 MB → 850 KB(減少 93%)
- ✅ 首屏時間縮短:10 秒 → 2 秒(提升 70%)
- ✅ 後續頁面載入:平均 300-500 KB per page
- ✅ 使用者體驗評分:從 45 分提升至 92 分(Lighthouse)
商業價值:
- 跳出率下降 35%
- 頁面停留時間增加 50%
- 轉換率提升 25%
面試重點
常見延伸問題:
-
Q: 為什麼不用 React.lazy() 或 Vue 的異步組件?
A: 我們確實有用 Vue 的異步組件(() => import()),但關鍵是三層架構:- 第 1 層(版型):編譯時決定(Vite 配置)
- 第 2 層(頁面):運行時 Lazy Loading
- 第 3 層(權限):導航守衛控制
單純的 lazy loading 只做到第 2 層,我們多做了版型層級的分離。
-
Q: 如何決定哪些程式碼該放在 main.js?
A: 使用 Vite 的manualChunks配置:build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'pinia', 'vue-router'],
'ui': ['element-plus'],
}
}
}
}原則:只有「每個頁面都會用到」的才放 vendor chunk。
-
Q: Lazy Loading 會不會影響用戶體驗(等待時間)?
A: 有兩個策略應對:- 預載(Prefetch):在閒置時預先載入可能訪問的頁面
- Loading 狀態:使用 Skeleton Screen 代替白屏
實際測試:後續頁面平均載入時間 < 500ms,用戶無感知。
-
Q: 如何測量優化效果?
A: 使用多種工具:- Lighthouse:Performance Score(45 → 92)
- Webpack Bundle Analyzer:視覺化分析 chunk 大小
- Chrome DevTools:Network waterfall、Coverage
- Real User Monitoring (RUM):真實用戶數據
-
Q: 有什麼 Trade-off(權衡)?
A:- ❌ 開發時可能遇到循環依賴問題(需要調整模組結構)
- ❌ 首次路由切換會有短暫載入時間(用 prefetch 解決)
- ✅ 但整體利大於弊,尤其對手機用戶體驗提升明顯