Vue3 - Router 路由參數與404頁面

tags: Vue
category: Front-End
description: Vue3 - Router 路由參數與404頁面
created_at: 2022/07/24 23:00:00

cover image


回到 手把手開始寫 Vue3


前言

有些框架所使用的路由可能預設就是使用正規表達式(Regexp)去做匹配的,但在這邊他的路由預設是以字串做匹配的,而如果需要使用正規表達式,就需要使用待會第一個提到的東西,路由參數。


路由參數


有時候可能需要動態的匹配一些網址,例如 /users/1/users/2 渲染的都是同樣的一個組件,但是我要收到不同的資料而去渲染不同的東西,而那個數字 12 就是路由參數。

在定義之前,一樣先建立一個 Component,好讓之後路由使用:

UserView.vue

<template>
  <h1>This is an user page</h1>
</template>

再來加上路由:

{
  path: "/users/:id",
  component: () => import("../views/UserView.vue"),
},

而這邊 : 開頭的東西就是路由參數,名字可以自己取,像這邊取作 id

而使用方式可以從 route.params 當中拿到那個 id ,像下面:

<template>
  <h1>This is an user page: {{ $route.params }}</h1>
</template>

這樣假設我去 /users/1 你會看到:

This is an user page: { "id": "1" }

那可能會想說,那如果我要在 script 當中寫,要怎麼拿到那個 $route 呢?

<script setup>
console.log($route);
</script>

因為上面這樣肯定會噴錯的。 ReferenceError: $route is not defined

這時就要使用 React hook很像的東西 useRoute 了,去拿到 route

<script setup>
import { useRoute } from "vue-router";

const route = useRoute();
console.log(route.params);
</script>

這樣你就會看 Console 看到輸出 {id: '1'} 了。

這時候為了方便測試,修改一下 App.vue 的內容,也順便幫路由定義加上 name 屬性:

{
  path: "/users/:id",
  name: "users",
  component: () => import("../views/UserView.vue"),
},
<script setup>
import { RouterLink, RouterView } from "vue-router";
</script>

<template>
  <nav>
    <RouterLink :to="{ name: 'users', params: { id: 1 } }">users 1</RouterLink>
    <RouterLink :to="{ name: 'users', params: { id: 2 } }">users 2</RouterLink>
  </nav>

  <div>
    <RouterView />
  </div>
</template>

這時候就可以單純用點的去切換頁面了。


內容改了但 script 只跑一次?

對,因為他使用同一個 Component,記得前幾篇應該有提到說如果能重複利用他就會盡量重複利用,所以他就沒有重新跑整個元件,所以當然你有生命周期也沒用,舉例來說:

onMounted(() => {
  console.log(route.params);
});

就算你加了上面這段他還是只會跑一次,因為他就被掛上去一次啊!


解決方案參考


1. 監聽路由參數

import { watch } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();

watch(
  () => route.params,
  (newParams, oldParams) => {
    console.log("hi 我變了,", oldParams, newParams);
  }
);

2. 使用路由守衛

你可以把他想成也是一種生命週期,不過他也可以取消你的轉址。

如果有寫過後端的人,其實就是路由的 Middleware

import { onBeforeRouteUpdate } from "vue-router";

onBeforeRouteUpdate((to, from) => {
  console.log("hi 我變了,", from.params, to.params);
});

至於取消轉址行為,看範例

onBeforeRouteUpdate((to, from) => {
  // 假設你做了一些判斷
  return false;
});

這時你會發現你點連結之後他不理你,但其實他有進去那個函數,不相信自己 Console.log 看看。

3. 為 RouterView 加上 key

這時就要回到你的 App.vue

<RouterView :key="$route.path" />

前面幾篇也有提到說當 key 變了他就會認定是不同東西然後重新渲染,所以如果他的 key 是路由的 path ,那你每次轉址他就會重新渲染,那麼理所當然就會觸發生命週期了。


使用正規表達式

路由參數可以使用正規表達式去限制要匹配的東西,只要在路由參數後面加上括號再定義就可以。

貼幾個官方範例上來看看

const routes = [
  // /:orderId -> 只接受數字
  { path: '/:orderId(\\d+)' },
  // /:productName -> 任何東西都吃
  { path: '/:productName' },
]

const routes = [
  // /:chapters -> 匹配一個或多個路由參數 /one, /one/two, /one/two/three, etc
  { path: '/:chapters+' },
  // /:chapters -> 匹配零個或多個路由參數 /, /one, /one/two, /one/two/three, etc
  { path: '/:chapters*' },
]

// 假設 { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 產出 /
router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
// 產出 /a/b

// 假設 { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 會噴錯,因為 `chapters` 不得為空


const routes = [
  // 只接受數字
  // 匹配 /1, /1/2, etc
  { path: '/:chapters(\\d+)+' },
  // 匹配 /, /1, /1/2, etc
  { path: '/:chapters(\\d+)*' },
]

const routes = [
  // 將匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 將匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

基本上就是正規表達式的那幾套。


404頁面

當知道了上面使用正規表達式的方式之後,再看官方給的 404 範例,應該就不會太難懂

const routes = [
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]

也就是那個 :pathMatch 其實也可以改成你喜歡的東西 XD




最後更新時間: 2022年07月24日.