本文 AI 產出,尚未審核

Vue3 路由系統(Vue Router 4)—— 程式化導航(router.pushrouter.replace


簡介

在單頁應用(SPA)中,路由是連結不同畫面與功能的核心。Vue Router 4 為 Vue 3 提供了完整且彈性的路由解決方案,而「程式化導航」則是讓我們在 JavaScript 內部自行控制頁面跳轉的關鍵技巧。

相較於在模板中直接使用 <router-link>,程式化導航允許 根據使用者行為、API 回傳結果或業務邏輯 動態決定要前往的路由。掌握 router.pushrouter.replace,不僅能提升使用者體驗,還能在表單提交、權限驗證、錯誤處理等情境中保持程式碼的可讀性與可維護性。

以下將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你深入了解 Vue Router 4 的程式化導航。


核心概念

1. router.pushrouter.replace 的差異

方法 目的 URL 變化 瀏覽器歷史紀錄
router.push(location) 新增一筆歷史紀錄,類似使用者點擊連結 會改變 window.location 保留前一頁,可使用「返回」
router.replace(location) 取代當前的歷史紀錄,常用於不想留下返回點的情境 會改變 window.location 不保留前一頁,返回會直接跳過此頁

簡單記憶法push入新紀錄;replace取代舊紀錄。

2. location 參數的寫法

router.push / router.replace 接受兩種形式的參數:

  1. 字串:直接寫路徑或具名路由的名稱
    router.push('/about')
    router.replace({ name: 'UserDetail', params: { id: 5 } })
    
  2. 物件:可同時提供 pathnameparamsqueryhash 等屬性
    router.push({
      name: 'Search',
      query: { q: 'Vue Router', page: 2 },
      hash: '#result'
    })
    

注意:若同時使用 pathname,Vue Router 會優先以 name 為主。

3. 等待導航完成

router.push / router.replace 皆回傳一個 Promise(Vue Router 4),可以在導航成功或失敗時取得回饋:

router.push('/dashboard')
  .then(() => console.log('導航完成'))
  .catch(err => console.error('導航失敗', err))

常見的失敗原因包括 重複導航(相同路由已在當前),或是 導航守衛 中的 next(false)

4. 與導航守衛(Navigation Guard)的配合

beforeEachbeforeEnter 等守衛裡,可以使用 next 直接返回 router.pushrouter.replace,實現 條件式重導

router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !store.state.isLoggedIn) {
    // 未登入,導向登入頁,使用 replace 避免回到受保護頁面
    next({ name: 'Login', replace: true })
  } else {
    next()
  }
})

程式碼範例

以下示範 5 個常見且實用的程式化導航情境,均以 Vue 3 + Composition API 為基礎。

範例 1️⃣:表單送出後導向明細頁(使用 push

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

const router = useRouter()
const name = ref('')
const email = ref('')

async function submitForm() {
  // 假設呼叫 API 建立使用者
  const res = await createUser({ name: name.value, email: email.value })
  if (res.success) {
    // 導向使用者明細頁,保留返回表單的紀錄
    router.push({ name: 'UserDetail', params: { id: res.data.id } })
  }
}
</script>

<template>
  <form @submit.prevent="submitForm">
    <input v-model="name" placeholder="姓名" />
    <input v-model="email" placeholder="Email" />
    <button type="submit">送出</button>
  </form>
</template>

說明:表單送出成功後使用 router.push,讓使用者可以透過瀏覽器「返回」回到表單頁面,方便修正資料。


範例 2️⃣:登入成功後使用 replace 防止返回登入頁

<script setup>
import { useRouter } from 'vue-router'
import { useStore } from '@/store'

const router = useRouter()
const store = useStore()
const credentials = reactive({ username: '', password: '' })

async function login() {
  const ok = await store.dispatch('auth/login', credentials)
  if (ok) {
    // 登入成功,使用 replace 替換當前的登入頁紀錄
    router.replace({ name: 'Home' })
  } else {
    alert('帳號或密碼錯誤')
  }
}
</script>

<template>
  <form @submit.prevent="login">
    <input v-model="credentials.username" placeholder="帳號" />
    <input v-model="credentials.password" type="password" placeholder="密碼" />
    <button type="submit">登入</button>
  </form>
</template>

說明:使用 replace 後,使用者按「返回」不會再回到登入頁,提升安全性與使用體驗。


範例 3️⃣:條件式導向(根據 API 回傳的狀態)

<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { fetchUserStatus } = '@/api'

const router = useRouter()

onMounted(async () => {
  const { status } = await fetchUserStatus()
  // 假設 status 可能是 'new', 'active', 'blocked'
  if (status === 'new') {
    router.replace({ name: 'Welcome' })   // 新使用者 → 歡迎頁
  } else if (status === 'active') {
    router.push({ name: 'Dashboard' })   // 正常使用者 → 主控台
  } else {
    router.replace({ name: 'AccessDenied' }) // 被封鎖 → 拒絕存取頁
  }
})
</script>

說明:在 onMounted 內根據後端回傳的使用者狀態決定導向路徑,replace 用於不希望留下「中繼」頁面的情況。


範例 4️⃣:帶參數與查詢字串的導航

// 在組件或服務中
function goToSearch(keyword, page = 1) {
  router.push({
    name: 'Search',
    query: { q: keyword, page },
    hash: '#results'   // 直接捲動到結果區塊
  })
}

// 呼叫
goToSearch('Vue Router', 3)

說明query 會自動序列化為 ?q=Vue%20Router&page=3hash 讓瀏覽器自動定位至 #results


範例 5️⃣:防止「重複導航」錯誤(使用 try...catch

async function safeNavigate(to) {
  try {
    await router.push(to)
  } catch (err) {
    // Vue Router 4 會拋出 NavigationDuplicated 錯誤
    if (err.name !== 'NavigationDuplicated') {
      console.error('導航失敗', err)
    }
    // 若是重複導航,可視需求不做任何事
  }
}

// 例:使用者點擊同一個列表項目
safeNavigate({ name: 'ProductDetail', params: { id: 42 } })

說明:當使用者多次點擊同一個連結時,router.push 會拋出 NavigationDuplicated,透過 try...catch 可避免不必要的錯誤訊息。


常見陷阱與最佳實踐

陷阱 說明 解決方案 / 最佳實踐
重複導航錯誤 (NavigationDuplicated) 同一路由多次 push 會拋錯 使用 try/catch 或在呼叫前比對 router.currentRoute
忘記傳遞 params 時使用 name params 只能與具名路由搭配,使用 path 會被忽略 永遠 使用具名路由 ({ name: 'User', params: { id: 1 } })
query 參數未編碼 手動拼接字串容易出錯 交給 Vue Router 處理,直接使用 query 物件
在守衛內使用 push 造成無限迴圈 若守衛內再次導向相同路由,會不斷觸發 使用 replace: true 或檢查 to.fullPath !== from.fullPath
未考慮瀏覽器返回時的狀態 push 會留下歷史紀錄,返回時可能需要重新載入資料 onBeforeRouteUpdate 中重新抓取資料,或使用 replace 取代不必要的紀錄

建議的開發流程

  1. 先決定導航意圖:是要保留返回紀錄 (push) 還是直接取代 (replace)。
  2. 使用具名路由:可讓 paramsquery 更安全、可讀性更佳。
  3. 統一錯誤處理:在一個共用的 navigate 函式裡包裝 router.push/replace,集中處理 NavigationDuplicated
  4. 結合守衛:在需要權限或資料前置檢查時,於守衛內使用 next({ name: ..., replace: true }),避免在組件內重複寫導向邏輯。
  5. 測試路由變化:使用 Vue Test Utils 的 router.push 模擬,確保導航流程在單元測試中也能正確執行。

實際應用場景

場景 使用方式 為何選擇 pushreplace
使用者完成註冊 註冊成功後導向「歡迎」頁,並保留返回至註冊表單的可能 push(讓使用者可回到表單修正)
OAuth 第三方登入 第三方授權成功回到應用後直接跳到原本想去的頁面 replace(避免回到授權回呼頁)
分頁或搜尋結果 點擊分頁或改變搜尋關鍵字時改變 URL query,保持瀏覽器前進/返回功能 push(每次變更都視為新紀錄)
錯誤頁面導向 API 回傳 404 時自動導向「找不到頁面」 replace(不想讓使用者返回錯誤的 API 呼叫)
多步驟表單 每完成一步即導向下一步的路由,使用 push 讓使用者可返回前一步 push(保留歷史)

總結

程式化導航是 Vue Router 4 中不可或缺的功能,router.pushrouter.replace 為我們提供了兩種不同的歷史紀錄行為,讓開發者可以依需求決定是否保留返回點。掌握 location 物件的寫法Promise 回傳、以及 與導航守衛的配合,能讓路由控制更彈性且不易出錯。

在實務開發中,建議遵循以下要點:

  1. 先思考意圖:是否需要返回?使用 pushreplace
  2. 優先使用具名路由,避免 params 失效。
  3. 統一錯誤處理,尤其是 NavigationDuplicated
  4. 合理使用守衛,在權限或資料前置檢查時直接重導。
  5. 測試與文件化,確保導航流程在不同情境下均可預期運作。

只要把這些概念與範例內化,你就能在 Vue 3 應用中自如地使用程式化導航,為使用者打造流暢且符合預期的單頁體驗。祝開發順利! 🚀