用 Vue Directive 管理角色權限

前言

最近在專案遇到的需求是依照角色分配不同權限,並依照權限顯示相應功能。

role 的值是在 login 後,從後端回傳 response 取得並存在 localStorage,用 Store 來管理角色狀態。

原本我是在頁面上使用 computed 來及時從 store 獲得角色值,然後再用 v-if 控制是否顯示。但後來發現更好的寫法!於是做此紀錄。如果有朋友有更好的做法歡迎分享~

備註: 以下程式碼都經過處理,沒有直接將專案程式碼複製貼上。只是要傳達做法,請依照各自專案再去配置。

原本的方法: v-if

  • 在 userStore 透過 localStorage 存取 Role 角色:
1
2
3
4
5
6
7
8
9
10
11
12
import { computed, ref } from "vue";
import { defineStore } from "pinia";

export const useUserStore = defineStore("user", () => {

/** 使用者資訊 */
const userInfo = ref({
role: localStorage.getItem("userRole") || "",
});

return { isLoggedIn, userInfo };
});
  • 在 utils 建立 hasRole 方法:
1
2
3
4
5
6
7
8
9
10
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/modules/user';

const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);

export const hasRole = (role: Role): boolean => {
const userRole = userInfo.value.role;
return role === userRole;
};
  • 在 views 引入 hasRole 方法,判斷是否為某角色,template 區域用 v-if 來決定顯示該組件顯示與否。
1
2
3
4
5
6
7
8
9
10
<template>
<MyButton v-if='!isPM'>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { hasRole } from '@/utils/role';

const isPM = computed(() => hasRole('PM'));
</script>

缺點

  • 如果多出一個角色,就要再寫一個 computed。角色管理四散在不同的 views 檔案中。
  • v-if 寫法較無判別性,難以即時排查。

使用 Directive 寫法

除了 Vue 原本提供的 v-if, v-show 等作法,我們也可以自己定義 v- 語法。

什麼情境適合

  • 處理重複的工作,例如我在不同組件上要依照角色顯示功能
  • 可全局註冊並套用在 DOM 上使用
  • 可指定生命週期(例如 mounted)

最終目標

只要在 views 的 components 加上 v-hide-for="['PM', 'RD']",只要用這兩種角色登入,就無法看到這個組件。全局通用,且不需要額外在 <script setup>定義。

1
2
3
<PrimaryButton
v-hide-for="['PM', 'RD']"`
/>

優點

  • 邏輯都放在 directives 集中處理,比較好維護跟擴充
  • 不用在每個 views 都用 computed 定義,而是全局通用
  • 看起來直覺多了!

官方文件 ref

1
2
3
4
5
const myDirective = {
// 在綁定元素的父組件
// 及他自己的所有子節點都掛載完成後調用
mounted(el, binding, vnode, prevVnode) {},
}

參數(都是可選的)

  • el:指令綁定到的元素。這可以用於直接操作 DOM。
  • binding:一個對象,包含各種屬性,我使用的就是 value:傳遞給指令的值。

實際作法

  1. 在 src 資料夾創造 directives 資料夾,創造 hideFor.ts 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import type { Directive } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/modules/user';

const hideForDirective: Directive = {
mounted(el, binding) {
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const excludedRoles = binding.value;

if (excludedRoles.includes(userInfo.value.role)) {
el.parentNode?.removeChild(el);
}
}
};

export default hideForDirective;

  1. 在 main.ts 做全局註冊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hideForDirective from '@/directives/hideFor';
import App from './App.vue';

async function setupApp() {
const app = createApp(App);

setupStore(app);

app.directive('hideFor', hideForDirective);

app.mount('#app');
}

setupApp();
  1. 隱藏你想隱藏的吧!

在 views 的組件上加 v-hide-for="['PM']",然後當我用 ‘PM’ 的角色登入後,我就看不到這個 Button 嚕。

<PrimaryButton
  v-hide-for="['PM']"`
/>

Hexo Icarus 架站紀錄

前言

開始習慣了 Markdown 寫文件後,越來越覺得 Medium 不適合用來整理技術筆記,還是另尋空間,並終於在 2025 初用 Hexo 架了自己的 blog。

Hexo 有很多開源主題,原先我是無腦跟著 胡立使用 Minos 這個主題,但後來發現安裝過程出現問題,且 Minos 的 GitHub 也在 2022 年進入 read-only 狀態,不開放提 issue,決定轉向其他還在維護中的 theme。

目前最熱門的 Hexo 主題除了 NexT 以外,也看到有人在推薦使用 Icarus。後者的星星數沒有 NexT 那麼高,以順眼程度來說我還是選擇了 Icarus。


流程

接下來紀錄我的架站過程。請記得用 node -v, npm -vgit 檢查工具是否已經安裝好,再進行後續步驟。


1. 安裝 Hexo

1-1. 全局安裝 hexo-cli

1
npm install -g hexo-cli

💡 提示:
Mac 使用者可以用 npm list -g --depth=0 看到已經被全局安裝。


1-2. 安裝 Hexo

<folder> 換成自己想要的名稱:

1
2
3
hexo init <folder>
cd <folder>
npm install

1-3. 檢查是否安裝完成

hexo serverhexo s 並到 http://localhost:4000 檢查,當看到預設的網站配置,代表安裝完成。

圖片


2. 推送至遠端 Git Repo

到這為止,可以在 GitHub 建立個人 repo 並且推送。
必須設為公開才能加入 Discussion 留言版功能。


3. 安裝 Icarus 主題

Icarus 官方 Blog “Getting Started with Icarus” (中文版) 提供兩種安裝方式: Install from source 或 Install via NPM。

我比較推薦用 Install from source (git clone),因為大部分的 Blog 教學都是以這個為基礎來做配置。若使用 npm install,Icarus 會被當作 NPM package 來安裝,資料夾結構會長得跟 git clone 不太一樣,發生問題也比較少資源可以查詢。


3-1. 使用 Install from source

官方部落格有介紹兩種方法,選一種使用就好了。

方法一:獨立的 repo (我是選這個)

1
git clone https://github.com/ppoffice/hexo-theme-icarus.git themes/icarus --depth 1

💡 提示:

  • 若指定版本,可使用 -b <version number>,不指定就是 latest 版本。
  • --depth 1 代表只下載最新的 commit,不包含完整的 commit 歷史。
  • 使用這個方法後,須透過 git rm --cached themes/icarus 移除嵌套的 git repo,之後使用 git add . 才不會出現錯誤訊息。

方法二:加入為 submodule

1
git submodule add https://github.com/ppoffice/hexo-theme-icarus.git themes/icarus

💡 提示:
加入後,使用 git submodule status 可以看到子模組的資訊。


3-2. 設定 theme

1
hexo config theme icarus

💡 提示:
這跟手動更改根資料夾中的 _config.yml 中的 theme: icarus 是一樣的意思。


3-3. 使用 hexo generate && hexo server 檢查安裝成功

image


💡 安裝過程中有遇到問題:
見這篇 GitHub Discussion