前一篇,我們完成了新增文章的頁面與流程,在網站開發的過程中,有些頁面是具有瀏覽的限制,例如,我們不希望一般的使用者能進到管理者頁面專用的頁面進行操作,這時候我們就需要做一些權限的驗證與限制,在 Nuxt 的頁面提供了路由中間件可以在我們導航至頁面之前,執行一些處理函數,就實作導航守衛 (Navigations Guards) 的效果。最後會介紹一下在 Nuxt 3 所提供的頁面和布局切換時的進度條 (Progress bar) 與轉場效果 (Transitions)。
首先,我們新增一個路由中間件 ./middleware/manage-auth.js
內容如下:
import { useUserStore } from '@/stores/user'
export default defineNuxtRouteMiddleware(() => {
if (process.client) {
const userStore = useUserStore()
if (!userStore.profile?.id) {
return navigateTo('/login')
}
} else {
return navigateTo('/')
}
})
在頁面元件中使用 definePageMeta()
來套用 manage-auth
中間件。調整 ./pages/manage/articles/create.vue
內容:
<script setup>
// ...
definePageMeta({
middleware: 'manage-auth'
})
</script>
我們首頁新增一個可以導向至 /manage/articles/create
頁面的按鈕。
<NuxtLink
class="text-md mt-12 rounded-sm bg-emerald-500 py-2 px-4 font-medium text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
to="/manage/articles/create"
>
前往撰寫文章
</NuxtLink>
當我們處於未登入的情況,點擊前往後,會經由中間件判斷 process.client
是否是在客戶端,進而從 User Store 取出使用者資訊進行判斷,當不存在 userStore.profile.id
時,表示未登入我們將導航至登入頁面 /login
;當登入完成後,就可以使用按鈕成功導航至新增文章的頁面。如果process.client
為 false
,表示導航是在伺服器端觸發的,例如,我們直接透過網址進入新增文章頁面,將會一律被重新導航至首頁。
當使用者在瀏覽網站時被引導或準備登入時,我們可以將使用者目前的頁面進行記錄,以便登入完成後,可以重新導向至使用者登入前的頁面,以此提供使用者更棒的體驗。
首先,我們新增一個路由中間件 ./middleware/logged-in-redirect.js
內容如下:
export default defineNuxtRouteMiddleware((to, from) => {
if (from && to.path !== from.path && !to.query.redirect_to) {
let redirectTo = null
if (from.query.redirect_to) {
redirectTo = from.query.redirect_to
from.query.redirect_to = undefined
} else {
redirectTo = from.fullPath
}
to.query.redirect_to = redirectTo
return navigateTo(to)
}
})
這個 logged-in-redirect
中間件的處理邏輯,我們接收 to
與 from
,分別為目標頁面與來源頁面,當使用者目標頁面 /login
還未帶上了 Query 參數 redirect_to
,我們就將來源的完整路徑添加上去 from.fullPath
,最後進行導向。
這裡需要進行判斷
redirect_to
,否則會重複發生重新導向。
在登入頁面中使用 definePageMeta()
來套用 logged-in-redirect
中間件。調整 ./pages/login.vue
內容:
<script setup>
// ...
definePageMeta({
middleware: 'logged-in-redirect'
})
</script>
在登入完成的地方,我們就可以使用 navigateTo()
導向至 redirect_to
給的頁面路徑。
<script setup>
const route = useRoute()
const handleEmailLogin = async () => {
// ...
navigateTo(route.query.redirect_to ?? '/')
}
</script>
完成後,我們從首頁點擊登入後.當登入完成後就會導向回首頁;而從文章頁面點擊登入,完成後則會導向回文章頁面。
Nuxt 3 提供一個 <NuxtLoadingIndicator>
元件,用作頁面導航後顯示載入的進度,會在頁面上方有一個進度條 (Progress bar)。
只需要將 <NuxtLoadingIndicator>
元件添加至 app.vue
或布局之中,調整 app.vue
內容如下:
<template>
<NuxtLayout>
<NuxtLoadingIndicator />
<NuxtPage />
</NuxtLayout>
</template>
元件可以傳入的屬性 (Props) 如下:
色碼
或repeating-linear-gradient()
函数,預設為 repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)
。px
,預設值為 3
。毫秒
,預設值為2000
。毫秒
,預設值為200
。當我們從首頁切換頁面時,網頁上方就會出現一個進度條,表示頁面正在載入中。
除了載入的進度條,頁面切換之間,也可以使用轉場效果 (Transitions) 來讓頁面之間的銜接更柔順,Nuxt 利用了 Vue 內建的 <Transition>
元件來幫助處理轉場和動畫,用以響應不斷變化的頁面與狀態。
Nuxt 預設為所有頁面 (Pages) 都設置了轉場,如果要啟用,請將以下 CSS 添加至 app.vue
中。
<template>
<NuxtPage />
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>
每個頁面的 pageTransition
預設屬性皆為 { name: 'page', mode: 'out-in' }
,name 為 page
也就對應了 CSS 類別的開頭;mode 有 in-out, out-in 及 default三種參數可選。
.[pageTransition.name]-enter-active,
.[pageTransition.name]-leave-active {
transition: all 0.4s;
}
.[pageTransition.name]-enter-from,
.[pageTransition.name]-leave-to {
opacity: 0;
filter: blur(1rem);
}
套用好頁面的轉場,就會有切換頁面時有模糊的效果。
既然知道 pageTransition
的 name 會對應頁面轉場的 CSS 名稱,我們就可以來自定義更多轉場,讓不同頁面套用不同的效果。
例如在 app.vue
添加 rotate 為前綴的類別名稱 CSS。
<style>
/* ... */
.rotate-enter-active,
.rotate-leave-active {
transition: all 0.4s;
}
.rotate-enter-from,
.rotate-leave-to {
opacity: 0;
transform: rotate3d(1, 1, 1, 15deg);
}
</style>
在頁面中使用 definePageMeta()
來設定 pageTransition.name
為 rotate
。調整 ./pages/login.vue
添加如下程式碼。
<script setup>
// ...
definePageMeta({
pageTransition: {
name: 'rotate'
}
})
</script>
當頁面切換時,皆會使用預設的模糊轉場效果,當切換至登入頁面就會套用指定的 rotate
頁面轉場,而有旋轉的轉場效果。
Nuxt 同樣為所有布局 (Layouts) 都設置了轉場,如果要啟用,請將以下 CSS 添加至 app.vue
中。
.layout-enter-active,
.layout-leave-active {
transition: all 0.4s;
}
.layout-enter-from,
.layout-leave-to {
filter: grayscale(1);
}
建立 ./layouts/teal.vue
布局,程式碼如下:
<template>
<div class="h-screen bg-teal-50">
<slot />
</div>
</template>
將登入頁面的布局套用 teal
。
<script setup>
// ...
definePageMeta({
layout: 'teal'
})
</script>
建立 ./layouts/green.vue
布局,程式碼如下:
<template>
<div class="h-screen bg-teal-50">
<slot />
</div>
</template>
將註冊頁面的布局套用 green
。
<script setup>
// ...
definePageMeta({
layout: 'green'
})
</script>
在登入頁面與註冊頁面切換時,因為兩個頁面使用了不同的布局,布局的轉場效果,使背影顏色會有灰階效果的轉場。
你也可以像自訂頁面轉場一樣,來使用 definePageMeta()
設定 layoutTransition.name
屬性,來指定自訂的轉場效果。
<script setup>
definePageMeta({
layout: 'green',
layoutTransition: {
name: 'slide-in'
}
})
</script>
頁面與布局的轉場效果,都可以透過 definePageMeta
來設定 pageTransition
或 layoutTransition
為 false
來禁止套用轉場效果。
<script setup>
definePageMeta({
pageTransition: false
layoutTransition: false
})
</script>
你也可以在 nuxt.config.ts
設置預設的頁面與轉場效果,例如:
export default defineNuxtConfig({
pageTransition: {
name: 'fade',
mode: 'out-in' // default
},
layoutTransition: {
name: 'slide',
mode: 'out-in' // default
}
})
當然要將所有頁面與布局預設禁用也可以設置如下:
export default defineNuxtConfig({
pageTransition: false,
layoutTransition: false
})
pageTransition
與 layoutTransition
接受的屬性可以參考 TransitionProps。
transition
在 app.vue
中使用 <NuxtPage />
時,你可以將 TransitionProps 作為元件的 Props 來啟用全域預設的轉場效果。
<template>
<div>
<NuxtPage :transition="{
name: 'bounce',
mode: 'out-in'
}" />
</NuxtLayout>
</div>
</template>
當使用此方法設定轉場效果時,就不能在頁面中使用
definePageMeta()
來覆蓋這裡的頁面轉場設置。
這篇我們主要實作了導航守衛,來為特定頁面添加瀏覽的權限,我們除了使用客戶端的 User Store 驗證外,也可以搭配 Cookie 再後端進行驗證,甚至為每個請求解析使用者,並查詢資料庫是否具有權限瀏覽,以此來控制使用者瀏覽頁面的權限,除了前端的阻擋外,更重要的是後端 API 也需要搭配進行權限驗證,才能有要的防止網頁漏洞產生,否則可能會發生,使用者無權瀏覽新增或管理的頁面,但是可以透過打 API 來進行相關操作,這樣是非常危險的。最後,我們將頁面切換時設置了進度條與轉場效果,使得整體網站能提供使用者更好的操作體驗,更多的轉場設置,也可以參考官方文件。
感謝大家的閱讀,這是我第一次參加 iThome 鐵人賽,請鞭小力一些,也歡迎大家給予建議 :)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。