Vue3 筆記 | Vue router 篇

閱讀時間約 18 分鐘

Vue-router,一個方便我們建立 SPA 網頁的套件。如同 Pinia 一樣,在建立 Vue 初始專案的時候系統就會自動詢問要不要添加 vue-router。

添加成功的話會在專案路徑 /src 下發現多一個 router 資料夾,裡面有個 index.js 是 Vue 為我們準備的路由範例,同時 main.js 中也會引入路由供全局使用。

main.js:

import './assets/main.css'

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

const app = createApp(App)

app.use(router)

app.mount('#app')

如果是舊專案要導入 vue-router,只需要 npm install vue-router 即可。



路由定義

怎麼定義路由?在 /src/router/index.js 下,官方已經定義了一組路由作為範例:

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
})

export default router

我們透過 createRouter 來建立整個路由架構,架構之下會有紀錄路由操作歷史的 history 和各項我們定義的路由。

history 項目,我們可以透過 createWebHistorycreateWebHashHistory 來建立歷史,雖然 Vue 推薦我們使用第一種 (SEO 問題),但他會需要後端協助在伺服器端做些調整,若你只是想簡簡單單做個佈署,用 hash 模式就好。

各項路由的定義最基本的有三個要素:pathnamecomponent

path 就是我們要自己定義的路由途徑,name 是路由的名字,通常會讓他跟 path 一樣,component 就是這個路由要對應的組件。

我們在導引路由的時候使用 to 做導引,後面接的可以是路由的 path 或是 name

另外我們可以看到官方在導入 home page 和導入 about page 用了不一樣的方式:

import HomeView from '../views/HomeView.vue'
/* …… */
component: HomeView
/* …… */
component: () => import('../views/AboutView.vue')

Home page 的元件導入是先在外部 import 進來然後直接放到 component 下。但 about page 是採用箭頭函式的 import 方法來把組件導入到 component 下,所以兩者差別在哪?

差別在於第二種箭頭函式的方法會在我們切換路由要使用它時才會被加載進來 (lazy load),相對於第一種方法可以避免因為一次加載過多東西導致畫面停頓使用者體驗不佳的地方。



導入路由

HTML 模板中導入路由

在 App.vue 的 <template>,我們會發現裡面有兩個東西:<RouterLink><RouterView>

<RouterLink> 添加 to 屬性加上我們設定的 pathname 可以導覽到我們設定的路由組件,實際它在編譯過後就是個 <a> tag。

<RouterView> 則是匹配路由的組件該渲染的位置,大可以把下面範例的 <RouterView> 移動到 <header> 上,會發現組件渲染位置改變。

<template>
<header>
<div class="wrapper">
<HelloWorld msg="You did it!" />

<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>

<RouterView />
</template>

<script setup> 中導入路由

今天如果是寫個函式,希望在它執行時一併幫我們做路由導引,由於我們並不能在 JavaScript 中寫 <RouterLink>,所以在這裡要使用 router.push 來做導引。

const navigateTo = () =>{
console.log(`I will navigate to about page`)
router.push({name: 'about'})
}

將這個函式綁上按鈕的點擊事件,當我點擊主頁按鈕時就會執行 console.log 並且導引到 about page。

而如果今天想要導引到某路由但不被記錄到歷史中,我們可以改用 replace 來做這件事:

router.replace({name: 'about'})

這個一樣會導引到 about page,但歷史的紀錄會忽略它。而如果要在 HTML 模板上使用,只需要在 <RouterLink> 上添加 replace 即可。

返回上一頁

嚴格上來說,是前往歷史中紀錄的第幾筆資料。剛剛提到 history 會記錄我們對路由的操作,所以我們就可以透過 router.go 來實現訪問上一頁或下一頁。

const goBack = ()=>{
console.log(`I will go back to main page`)
router.go(-1)
}

把它綁上一個 about page 的按鈕,那當點擊時就會前往上一頁,也就是 main page。



路由上的參數

路由參數 (params)

又叫做動態路由,以 : + 參數的方式出現。

會在哪裡看到動態路由?比如一個部落格上每篇文章的網址,或是電影網站上每部電影的頁面。這些情況因為通常只更改內部資料的渲染所以都會靠迴圈直接套用組件與路由。

我們可以先為一個 TryParams 元件建立它的導覽路由,一樣在 index.js 中做設定:

{
path: '/try/:id',
name: 'tryParams',
component: () => import('../components/TryParams.vue')
}

然後我們前往父元件 App.vue 用 v-for 建立一個簡單的動態路由導覽列:

<template>
<ul>
<li v-for="i in 5" :key="i">
<RouterLink :to="`/try/${i}`"> I an page: {{ i }} </router-link>
</li>
</ul>

<RouterView />
</template>

接著我們前往 TryParams.vue 做些設定,我想讓每個頁面都渲染出屬於他們自己的動態 id,並且監控在每次動態 id 改變時都拋出一個 console.log 在後台,可以這樣做:

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

const route = useRoute();

watch(() => route.params, (newParams, oldParams) => {
console.log(`id changed from ${oldParams.id} to ${newParams.id}`);
});
</script>

<template>
<div>Here is page {{ route.params.id }}</div>
</template>

簡單來說,我們就是透過 route.params 在取路由參數。如果只是單純應用在 HTML 模板中,我們可以直接寫 $route.params.id,但在 <script setup> 中就得先引入 useRoute 了。

現在可以看到用同一個組件一動態路由渲染出不一樣的畫面。

raw-image

查詢參數 (query)

這個參數的存在意義在於它所傳遞的是一對 key-value 參數,通常我們會在搜索欄輸入我們的關鍵字按下搜索後,就會看到網址的後面用 ? 帶上我們剛剛輸入的關鍵字,這就是 query

打個比方:/search?page=1&keyword=vue

props 傳遞參數

參數可以用 props 做傳遞,上述動態參數的程式碼改成這樣會精簡很多。

Index.js:

{
path: '/try/:id',
name: 'try',
component: () => import('../components/TryParams.vue'),
// 添加 props
props: true
}

tryParams:

<script setup>
// 定義 props
defineProps(['id'])
</script>

<template>
<div>Here is page {{ id }}</div>
</template>



匹配路由

404 頁面

假設一個情況,當使用者今天想去 /about 但它手滑打成 /about/1,但我們沒有定義這個路由的組件啊!所以使用者只能看到一片白…。

解決這種情況就會用匹配路由的方式來解決:

{
path: '/about/:pathMatch(.*)*',
name: '404',
component: NotFound
}

我們用正規表達式表示途徑 /about 後面接任何東西通通給我導向 NotFound 這個組件。

raw-image

依匹配動態渲染畫面

可能有一種情況是,在同一條路由下想要渲染複數種畫面 (就是使用不一樣的組件),最簡單的方法是給予他們各自不同的靜態路由,比如:/about/a/:id 和 /about/b/:id 或是寫個巢狀路由。

但就是有時會嫌為了區分還要多打 a 和 b 很麻煩,那同樣可以用匹配路由的方式幫忙完成。

例如,我想要 /about 途徑後街的是數字時渲染 NotFound 組件,其他情況下渲染 AboutView 組件,可以這樣寫:

{
path: '/about/:afterAbout',
component: AboutView
},
{
path: '/about/:afterAbout(\\d+)',
component: NotFound
}



重定向與別名

重定向

顧名思義就是從 A 路由轉址到另一個 B 路由去。因為 A 路由途徑下根本不會被渲染,所以連 component 都可以不要定義。

{
path: '/try',
name: 'try',
redirect: {name: 'home'}
}

別名

別名跟重定向類似,但重定向比較像是你找 A 辦手續,但 A 跟你說要找 B。別名則是 A 的另一個名字就叫做 B,所以不管輸入哪一個路由都會對應到同個組件。

{
path: '/',
name: 'home',
alias: ['/homepage', '/mainpage'],
component: HomeView
}

這串的意思是:我乃 /home,偉大的 /homepage 暨尊爵不凡的 /mainpage!



巢狀路由

簡單來說就是一個 <RouterView> 中再放一個 <RouterView>

這個的實作意義在於我們可能有兩個組件要渲染在父組件之下,比如說 /about/people 那就在 about 組件下渲染 people 組件,而 /about/number 就在 about 組件下渲染 number 組件。

AboutVue 組件:

<template>
<div class="about">
<h1>This is an about page</h1>
</div>
<RouterView/>
</template>

Index.js:

{
path: '/about',
component: AboutView,
children: [
{
path: 'people',
component: () => import('../components/People.vue')
},
{
path: 'number',
component: () => import('../components/Number.vue')
}
]
}



導航守衛

Vue router 提供了四個函數用來幫助切換頁面時進行一些操作或檢查。如果把路由想像成公路,導航守衛就是每條公路上的警察。

  1. 全局前置守衛 (beforeEach):會在每次路由切換之前執行,這項功能都常用在換頁的時候檢查使用者權限是否足以訪問該頁面。
  2. 全局解析守衛 (beforeResolve):在路由確定之前執行,通常用來確保所有內容都已加載完畢。
  3. 全局後置守衛 (afterEach):在每次路由切換之後執行,可以用來記錄用戶行為,或在頁面切換後做些操作。
  4. 路由獨享守衛 (beforeEnter):在特定路由設定中使用,用於對該路由進行獨立的前置檢查或操作。

要注意的是,在 <script setup> 寫法中,beforeRouteLeavebeforeRouteUpdate 要改成 onBeforeRouteLeaveonBeforeRouteUpdate



路由轉場動畫

Vue 的轉場 <Transition> 組件同樣可以使用在路由的轉場上,但我們必須在 <RouterView> 上使用 v-slot

<RouterView v-slot="{ Component }">
<transition name="fade">
<component :is="Component" />
</transition>
</RouterView>



滾動行為

我們可以在創建路由時多添加一個 scrollBehavior 項目,用來管理滾動行為,比如說下面這一串:

const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
}
})

當返回上一頁時,畫面會出現在你上次瀏覽的位置。



參考資料

  1. vue-router 官方文件
  2. Vue3 + Vite 快速上手 Get Startrd EP8 - Vue-Router 基礎入門,SPA網頁輕鬆上手 !
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
發表第一個留言支持創作者!
Jeremy Ho的沙龍 的其他內容
Vue3 學習筆記,用 Pinia 管理資料與邏輯
Vue3 學習筆記,元件與資料傳遞篇
Vue3 學習筆記,專案建立與基礎響應式篇
AWS 佈署簡單操作以及 RDS 建立篇
AWS 部署學習 - Day1,註冊與安裝 EB
記錄一下 API 串接的四種方式 www
Vue3 學習筆記,用 Pinia 管理資料與邏輯
Vue3 學習筆記,元件與資料傳遞篇
Vue3 學習筆記,專案建立與基礎響應式篇
AWS 佈署簡單操作以及 RDS 建立篇
AWS 部署學習 - Day1,註冊與安裝 EB
記錄一下 API 串接的四種方式 www
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在 Vue 應用中直接使用 .reverse() 來修改陣列可能在開發環境中未出現問題,但在生產環境中卻可能導致重新渲染錯誤及資料順序不一致的問題。建議使用 .slice() 創建淺拷貝的解決方案,以確保 Vue 的反應性系統能正常運作並避免應用當機。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
這篇文章記錄了如何在網頁中使用<iframe>或套件vue3-google-map來實現Google Map呈現,並在上面設置圖釘。兩種方法的優缺點也有相關的說明。想要在網頁上加入Google Map及設置圖釘的讀者可以參考這篇文章。
Thumbnail
前言 Vue 是一個現代開發框架,擁有完尚的生態系,讓我們可以將須多元件客製化,做出組件,並且可重複利用,高擴充性。在開發組件時,每個組件都擁有自己的生命周期,Vue 組件會偵測每個變數值,是否有變,並且更新內容,今天要一個一個了解 Vue 的生命週期,讓大家有更多認識。 Vue 的生命週期
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
父元件 傳遞變數時須加上冒號 子元件 接收props用法如下 本筆記參考: 1. https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2 2. https://juejin.cn/post/7
Thumbnail
雖然距離上次Vue直播班課不到一年,但看到這次的課程有Pinia內容,手又不小心刷了魔法小卡(? 意識到自己的成長應該是可以輕鬆地串接API,畢竟去年也已經串到可以去烤串店了XD,就算是重新複習,還是可以從中獲得新的成長,而且發現到老師一年講的比一年還好,今年有很多觀念講得更清楚了! 可惜這次第六週
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
在 Vue 應用中直接使用 .reverse() 來修改陣列可能在開發環境中未出現問題,但在生產環境中卻可能導致重新渲染錯誤及資料順序不一致的問題。建議使用 .slice() 創建淺拷貝的解決方案,以確保 Vue 的反應性系統能正常運作並避免應用當機。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
這篇文章記錄了如何在網頁中使用<iframe>或套件vue3-google-map來實現Google Map呈現,並在上面設置圖釘。兩種方法的優缺點也有相關的說明。想要在網頁上加入Google Map及設置圖釘的讀者可以參考這篇文章。
Thumbnail
前言 Vue 是一個現代開發框架,擁有完尚的生態系,讓我們可以將須多元件客製化,做出組件,並且可重複利用,高擴充性。在開發組件時,每個組件都擁有自己的生命周期,Vue 組件會偵測每個變數值,是否有變,並且更新內容,今天要一個一個了解 Vue 的生命週期,讓大家有更多認識。 Vue 的生命週期
父元件 傳遞方法使用@ <template>    ...    <Login @modalClose="modalClose"/> ... </template> <script setup>     const _modal = ref();     function m
父元件 傳遞變數時須加上冒號 子元件 接收props用法如下 本筆記參考: 1. https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2 2. https://juejin.cn/post/7
Thumbnail
雖然距離上次Vue直播班課不到一年,但看到這次的課程有Pinia內容,手又不小心刷了魔法小卡(? 意識到自己的成長應該是可以輕鬆地串接API,畢竟去年也已經串到可以去烤串店了XD,就算是重新複習,還是可以從中獲得新的成長,而且發現到老師一年講的比一年還好,今年有很多觀念講得更清楚了! 可惜這次第六週