☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
vue 中級

🧭 Vue Router:單頁應用路由管理

📌 什麼是 SPA(單頁應用)?

傳統網站 = 每次點連結,瀏覽器重新載入整個頁面 SPA = 只載入一次 HTML,之後「換頁」只是用 JavaScript 切換顯示的元件

傳統網站(Multi-Page Application):
  點「關於」→ 瀏覽器送請求 → 伺服器回傳 about.html → 整頁重新載入

SPA(Single Page Application):
  點「關於」→ JavaScript 攔截 → 切換顯示 About 元件 → 網址列更新
  ❌ 不重新載入頁面
  ✅ 使用者體驗更流暢

Vue Router 就是 Vue 官方提供的 SPA 路由管理工具。它不是瀏覽器原生功能,而是用 JavaScript 的 History API 封裝出來的。


📌 安裝與基本設定

# 用 npm 安裝(Vite 專案)
npm install vue-router@4

定義路由:router/index.js

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

// 引入頁面元件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import NotFound from '@/views/NotFound.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  // 動態路由:用冒號 :id 定義參數
  {
    path: '/user/:id',
    name: 'UserProfile',
    component: () => import('@/views/UserProfile.vue'), // 懶載入
    props: true // 將路由參數作為 props 傳入
  },
  // 巢狀路由
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue'),
    children: [
      { path: '', component: () => import('@/views/DashboardHome.vue') },
      { path: 'settings', component: () => import('@/views/DashboardSettings.vue') },
      { path: 'profile', component: () => import('@/views/DashboardProfile.vue') }
    ]
  },
  // 404 頁面(萬用路由,放最後)
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: NotFound
  }
]

const router = createRouter({
  // createWebHistory 用的是瀏覽器的 History API
  // 這是原生 JS 的 API,Vue Router 只是封裝它
  history: createWebHistory(),
  routes
})

export default router

掛載到 Vue App:main.js

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router) // 安裝 Vue Router
app.mount('#app')

App.vue 中使用

<template>
  <div id="app">
    <nav>
      <!-- router-link 取代 <a> 標籤 -->
      <router-link to="/">首頁</router-link>
      <router-link to="/about">關於</router-link>
      <router-link :to="{ name: 'UserProfile', params: { id: 1 } }">
        個人檔案
      </router-link>
    </nav>

    <!-- router-view 是路由元件的顯示區域 -->
    <router-view />
  </div>
</template>

📌 動態路由與取得參數

<!-- views/UserProfile.vue -->
<template>
  <div>
    <h1>使用者 #{{ userId }}</h1>
    <p>查詢參數 tab:{{ tab }}</p>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'

// useRoute() 取得目前路由資訊
const route = useRoute()
// useRouter() 取得路由器實例(可以用來導航)
const router = useRouter()

// 路由參數 /user/:id → route.params.id
const userId = computed(() => route.params.id)

// 查詢參數 /user/1?tab=posts → route.query.tab
const tab = computed(() => route.query.tab || 'info')

// 程式化導航
function goHome() {
  router.push('/')
}

function goToUser(id) {
  router.push({ name: 'UserProfile', params: { id } })
}

function goBack() {
  router.back() // 等同於瀏覽器上一頁
}
</script>

導航守衛就像「門衛」,在路由切換前做檢查。

全域守衛

// router/index.js
router.beforeEach((to, from, next) => {
  // to: 要去的路由
  // from: 來自的路由
  // next: 放行函式

  const isLoggedIn = !!localStorage.getItem('token')

  // 需要登入的頁面
  if (to.meta.requiresAuth && !isLoggedIn) {
    // 導向登入頁,並記住原本要去的頁面
    next({ path: '/login', query: { redirect: to.fullPath } })
  } else {
    next() // 放行
  }
})

// 路由定義加上 meta
const routes = [
  {
    path: '/dashboard',
    component: Dashboard,
    meta: { requiresAuth: true } // 需要登入
  },
  {
    path: '/login',
    component: Login,
    meta: { requiresAuth: false }
  }
]

元件內守衛

<script setup>
import { onBeforeRouteLeave } from 'vue-router'

// 離開頁面前確認
onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    const answer = window.confirm('有未儲存的變更,確定離開嗎?')
    if (!answer) return false // 取消離開
  }
})
</script>

📌 路由懶載入 (Lazy Loading)

// ❌ 全部打包在一起(首次載入慢)
import Home from './views/Home.vue'
import About from './views/About.vue'
import Dashboard from './views/Dashboard.vue'

// ✅ 懶載入:只在需要時才載入(動態 import 是原生 JS 語法!)
const routes = [
  { path: '/', component: () => import('./views/Home.vue') },
  { path: '/about', component: () => import('./views/About.vue') },
  {
    path: '/dashboard',
    // 還可以自訂 chunk 名稱
    component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue')
  }
]

路由載入動畫

<template>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <Suspense>
        <component :is="Component" />
        <template #fallback>
          <div class="loading">載入中...</div>
        </template>
      </Suspense>
    </transition>
  </router-view>
</template>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
</style>

📌 完整範例:多頁面導航

<!-- App.vue -->
<template>
  <div>
    <nav class="navbar">
      <router-link
        v-for="link in navLinks"
        :key="link.path"
        :to="link.path"
        active-class="active"
      >
        {{ link.icon }} {{ link.label }}
      </router-link>
    </nav>

    <main>
      <router-view />
    </main>
  </div>
</template>

<script setup>
const navLinks = [
  { path: '/', label: '首頁', icon: '🏠' },
  { path: '/courses', label: '課程', icon: '📚' },
  { path: '/dashboard', label: '儀表板', icon: '📊' },
  { path: '/about', label: '關於', icon: 'ℹ️' }
]
</script>

💡 小提醒

  • router-link<a> 好,因為不會重新載入頁面
  • 動態路由參數變化時,元件不會重新建立——用 watch 監聽 route.params
  • 路由懶載入用的 import()ES Module 原生語法(這個真的是 JS 的!)
  • useRouteuseRouter 是 Vue Router 的 API,不是 JS 原生的

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊