在 Day 3 我們說明了使用 Pinia 來管理應用程式的狀態,例如登入狀態與使用者角色。
但光有「狀態管理」還不夠,因為系統需要根據不同角色,決定誰可以看到什麼頁面。
這時候就要引入 Vue Router —— Vue 官方的路由管理工具。
今天我們會學習:
在目前的 prototype 中,每次點擊一個連結(例如從 dashboard.html
到 order-management.html
),瀏覽器都會重新載入整個頁面。
這種傳統的 多頁面應用 (MPA, Multi-Page Application),每次換頁都要重新請求 HTML、CSS、JS,容易造成體驗不佳,也浪費資源。
在深入了解 Vue Router 之前,我們先來理解「路由」這個概念。
路由(Routing)就是 URL 與頁面內容的對應關係:
網站首頁 (/
├── 首頁 (/home)
├── 關於我們 (/about)
├── 購物車 (/cart)
│
└── 商品列表 (/products)
│
├── 服飾類 (/products/clothing)
│ ├── T恤商品頁 (/products/clothing/tshirt-001)
│ ├── 牛仔褲商品頁 (/products/clothing/jeans-002)
│ └── 外套商品頁 (/products/clothing/jacket-003)
│
└── 3C類 (/products/electronics)
├── 手機商品頁 (/products/electronics/phone-001)
├── 筆電商品頁 (/products/electronics/laptop-002)
└── 耳機商品頁 (/products/electronics/headphone-003)
路由系統就像指示牌,告訴你:
/home → 看到首頁
/products/clothing → 看到服飾類商品
/products/clothing/tshirt-001 → 看到特定的T恤詳細資訊
/cart → 看到購物車
瀏覽器透過不同的網址,向後端伺服器發送 Request,伺服器接收到瀏覽器的請求後,回應對應的內容給瀏覽器來渲染,這種交互的機制就可以稱為後端路由(Backend routing),而管理後端路由的程式通常會被稱為 Router。
Vue Router 是 Vue 官方提供的 路由管理工具。
它的核心功能是:
SPA 的核心在於:整個網站其實只有一個主要的 HTML 檔,頁面切換都是透過 JavaScript 即時渲染元件來完成的。
換句話說,使用者在切換頁面時,並不會重新載入整個 HTML,而是由前端框架(例如 Vue)根據路由來顯示不同的內容。
這和傳統的 MPA (Multi-Page Application,多頁式應用) 差異如下:
特性 | MPA (Multi-Page Application) | SPA (Single-Page Application) |
---|---|---|
頁面結構 | 多個獨立的 HTML 頁面,每次換頁都重新載入 | 一個主要 HTML,透過前端框架切換元件 |
頁面切換 | 瀏覽器整個刷新,重新下載資源 | 僅更新畫面需要的區塊,頁面不刷新 |
速度 | 較慢,每次換頁都要重新請求 JS、CSS、HTML | 較快,資源一次載入即可重複使用 |
使用者體驗 | 容易卡頓,中斷操作流程 | 流暢,體驗接近原生 App |
狀態管理 | 換頁後,前端狀態通常會消失 | 狀態能持續存在於前端,跨頁共享 |
SEO | 天然友好(有完整 HTML) | 需 SSR / 預渲染處理 |
依據上面表格的對比可以發現 SPA 對於 SEO 是比較不友善的,但這個 銷售系統是供內部使用的,SEO 並不是我們的優先考量。
相反的,我們更在乎以下幾點:
操作體驗的流暢度
業務或店長每天都會在系統中頻繁操作,SPA 能確保換頁時不會整個刷新,避免不必要的等待,效率更高。
狀態持續存在
使用者登入後的身分、角色、當前選擇的門市等狀態,可以在不同頁面間保留,不必重複載入或丟失。
減少伺服器負擔
傳統 MPA 每次換頁都要回頭跟伺服器要 HTML,SPA 則是一次載入主要框架,之後多數操作都在前端完成,伺服器只需提供 API 資料。
更好擴展性
系統後續會隨著角色權限、功能模組擴充,SPA 架構更方便維護與拓展。
這邊我們會將系統中的所有頁面對應到一條條 URL,繪製出「路由地圖」。
index.html
→ /login
dashboard.html
→ /dashboard
order-management.html
→ /orders
order-detail.html
→ /orders/:id
(:id
是動態參數,可以匹配任何訂單編號)
const routes = [
{ path: '/login', component: Login },
{ path: '/dashboard', component: Dashboard },
{ path: '/orders', component: OrderManagement },
{ path: '/orders/:id', component: OrderDetail },
// ...
]
在銷售系統中有「業務」這個角色,但系統中也可能存在「店長」或「系統管理員」等更高權限的角色。
問題:
如何防止「業務」直接輸入 URL,例如 /system-admin-settings 或 /sales-reports,進入他們 不該看到的頁面?
單純在介面上 隱藏連結是不夠的,必須在路由層級做權限管控。
這時就可以使用導航守衛和路由元資訊
1. 路由元資訊 (Route Meta Fields)
在每條路由中,我們可以透過 meta 屬性,定義需要的條件,例如:是否需要登入、允許的角色。
{
path: '/sales-reports',
component: SalesReports,
meta: { requiresAuth: true, roles: ['店長', '系統管理員'] }
}
2. 導航守衛 (Navigation Guards)
vue-router 提供了 beforeEach,會在 每次路由跳轉前 被觸發,這就是我們的「守衛」。
程式碼範例
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
// Step 1: 檢查是否需要登入
if (to.meta.requiresAuth && !userStore.isLoggedIn) {
return next('/login')
}
// Step 2: 檢查角色權限
if (to.meta.roles) {
const userRole = userStore.currentUser.role
if (!to.meta.roles.includes(userRole)) {
return next('/dashboard') // 或導向「權限不足」頁
}
}
next()
})
檢查目標路由 to
是否需要登入 (meta.requiresAuth
)。
userStore.isLoggedIn
/login
若已登入 → 檢查該路由是否需要特定角色 (meta.roles
)。
userStore.currentUser.role
取得角色根據檢查結果:
next()
/dashboard
今天,我們完成了從「多頁應用」到「單頁應用」的架構轉變
我們不再讓使用者在頁面間不斷重新載入,而是透過 Vue Router 打造良好的導航體驗。
更重要的是,我們建立了完整的權限防護機制,確保每個角色都只能看到該看的頁面。
明日,我們將進入 [Vueの呼吸・肆之型] API設計 - 前後端分離的介面規劃,讓這個銷售系統真正活起來 —— 全集中🔥
參考資料:
https://book.vue.tw/CH4/4-1-vue-router-intro.html