MUKI AI Summary
Vue 支援自定義指令,如 v-role
,可用來設計網站權限。只需在 template 中使用 v-role
設定會員權限即可。這些指令可應用於 router,實現選單的顯示與隱藏,並可連接後端資料庫動態修改會員權限。
在 main.ts
中定義指令,並封裝後匯入。使用 pinia
管理 roles data
,或暫存於 localStorage
。設置 router meta 定義不同權限的訪問路由,使用 nav guard 確保未授權用戶被導回 /home
。...
我們常用的 v-on
, v-model
... 等 v-*
的語法,都是屬於 Vue 的指令,但大家有想過,我們是否能自己定義這個指令呢?例如發展成 v-muki
, v-mukispace
... 這樣的自訂指令?
答案是可以的!
我最近也用了 v-*
自定義指令,做了一個 v-role
的權限設計,只要在 template 這樣寫:
<template> <div v-role="['basic']">權限為 basic 的會員才看得到</div> <div v-role="['free']">權限為 free 的會員才看得到</div> <div v-role="['basic', 'free']">權限為 basic 或 free 的會員才看得到</div> </template>
此外也可以應用在 router,讓選單對應隱藏 / 顯示,也能跟後端資料庫串接,即時修改會員的權限。
使用 Vue-* 自定義指令
首先要在 main.ts
定義自己的指令,不過考量到未來可能不只一組自定義指令,所以我選擇的方法是封裝後再匯入,這樣未來如果要開發更多組自定義指令,會比較方便與乾淨
▼ 先在 main.ts
匯入一個管理自定義指令的檔案
import directives from '@/directives/index' app.use(directives)
▼ 新增剛剛匯入的檔案 (directives/index.ts
)
import { App } from 'vue' import role from './modules/role' // 這邊再匯入一個新的模組檔,就是我要定義的指令內容 const directiveList: any = { role } const directives = { install: function (app: App<Element>) { Object.keys(directiveList).forEach(key => { app.directive(key, directiveList[key]) }) } } export default directives
▼ 再新增一個 modules/role.ts
檔案
import { useUserStore } from '@/stores' import type { Directive, DirectiveBinding } from 'vue' interface ElType extends HTMLElement { parentNode: any } const role: Directive = { mounted(el: ElType, binding: DirectiveBinding) { const store = useUserStore() const { value } = binding // 該會員權限從 store 取得,我用的是 pinia 套件 (store.roles type : array) const roles = store && store.roles if (value && value instanceof Array && value.length > 0) { const requiredRoles = value const hasRole = roles.some(role => { return requiredRoles.includes(role) }) // 如果沒有這個權限,就移除整個 HTML 節點 if (!hasRole) { el.parentNode && el.parentNode.removeChild(el) } } else { throw new Error('error') } } } export default role
我把 roles data
存在 pinia store
,如果沒有用 pinia
或是想先測試一下看看,可以選擇先存在 localStorage
。
記得一定要先有 roles data
才能在 Vue component 測試效果喔,不然他會找不到你對應的權限
<template> <div v-role="['basic']">權限為 basic 的會員才看得到</div> <div v-role="['free']">權限為 free 的會員才看得到</div> <div v-role="['basic', 'free']">權限為 basic 或 free 的會員才看得到</div> </template>
在 router 設定存取權限
假設我希望免費會員,不能瀏覽 /no-free
這個網頁,假設他因為某些原因輸入了這個網址,我必須導回首頁 /home
設定 router meta
首先先設定 router meta,讓我們知道哪些權限的人,可以瀏覽哪些路由位置。
{ name: 'Home', meta: { title: 'home', roles: ['vip', 'basic', 'free'] // 這一頁 vip, basic, free 都可以瀏覽 }, path: '/home', component: () => import('@/views/Home.vue'), }, { name: 'NoFree', meta: { title: 'No Free', roles: ['vip', 'basic'] // 這一頁只有 vip, basic 可以瀏覽 }, path: '/no-free', component: () => import('@/views/NoFree.vue'), }
設定 nav guard 導航守衛
判斷權限的寫法,跟 role.ts
一樣
router.beforeEach(async (to, from, next) => { let hasRole = null if (to.meta && to.meta.roles instanceof Array && to.meta.roles.length > 0) { const requiredRoles = to.meta.roles hasRole = roles.some(role => { return requiredRoles.includes(role) }) } if (hasRole !== null && !hasRole) { next('/home') } else { next() } })
以上這個方法就是很簡單的,用來判斷有沒有這個權限,沒有的話就導回 /home
的寫法。
但我相信大家的 nav guard 應該還有其他的判斷,所以請記得要把這些判斷全部考慮進去唷
有任何問題也歡迎留言告訴我,謝謝