使用位運算來做使用者鑑權其實並不是一件新鮮事,已經有不少人講過了。不過最近在看vue3原始碼的時候發現vue3在對VisualDOM做patch操作的時候竟然也使用了位運算進行flag的判斷,便忽然來了興趣,想要好好說道說道。
首先看看來看看vue3原始碼,已經去除了不必要的註釋
patchFlags是VisualDOM中對vnode的型別標記,在更新DOM樹的時候會根據vnode的型別來使用不同的更新策略,這裡不展開說了,我們主要看這裡對型別的定義。
(patchFlags.ts Github原始碼地址)
// Patch flags can be combined using the | bitwise operator and can be checked
// using the & operator, e.g.
//
// const flag = TEXT | CLASS
// if (flag & TEXT) { ... }
//
// Check the `patchElement` function in './renderer.ts' to see how the
// flags are handled during diff.
export const enum PatchFlags {
TEXT = 1,
CLASS = 1 << 1,
STYLE = 1 << 2,
PROPS = 1 << 3,
FULL_PROPS = 1 << 4,
HYDRATE_EVENTS = 1 << 5,
STABLE_FRAGMENT = 1 << 6,
KEYED_FRAGMENT = 1 << 7,
UNKEYED_FRAGMENT = 1 << 8,
NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10,
// SPECIAL FLAGS -------------------------------------------------------------
// Special flags are negative integers. They are never matched against using
// bitwise operators (bitwise matching should only happen in branches where
// patchFlag > 0), and are mutually exclusive. When checking for a special
// flag, simply check patchFlag === FLAG.
HOISTED = -1,
BAIL = -2
}
// dev only flag -> name mapping
export const PatchFlagNames = {
[PatchFlags.TEXT]: `TEXT`,
[PatchFlags.CLASS]: `CLASS`,
[PatchFlags.STYLE]: `STYLE`,
[PatchFlags.PROPS]: `PROPS`,
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
[PatchFlags.HYDRATE_EVENTS]: `HYDRATE_EVENTS`,
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
[PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
[PatchFlags.HOISTED]: `HOISTED`,
[PatchFlags.BAIL]: `BAIL`
}
可以看到,除了最後兩種特殊型別外共有11種型別,每一種的值都是依次將1左移一位得到的。
在文件開頭的註釋裡作者甚至貼心的給出了用法:使用“|”來進行組合賦值,使用“&”來進行檢查。
下面我們來寫一個簡單的例子
以常用的Linux命令chmod 777為例,7代表可讀可寫可執行,寫成二進位制就是0111。具體如下:
許可權 | 十進位制 | 二進位制 |
---|---|---|
x (執行) | 1 | 0001 |
w (寫入) | 2 | 0010 |
r(讀取) | 4 | 0100 |
其中,每一種許可權的數值就是將1左移1到3位得到的。
不難理解,如果是可讀可執行則對應0101,值為5;可寫可執行對應0011,值為3,就此我們來寫一個簡單的鑑權系統。
const Permissions = {
X: 1,
W: 1 << 1, // 0010 -> 2
R: 1 << 2, // 0100 -> 4
};
let userPermission = 0; // 初始為0,即無許可權
/** 賦權 */
userPermission |= Permissions.X; // 賦予可執行許可權,此時 userPermission 為 0001
userPermission |= Permissions.W; // 賦予寫許可權,此時 userPermission 為 0011
/** 鑑權 */
if ( userPermission & Permissions.X ) { // 0011 & 0001 結果為1,返回真
console.log('此使用者有可執行許可權');
}
if ( userPermission & Permissions.W ) { // 0011 & 0010 結果為2,返回真
console.log('此使用者有寫許可權');
}
if ( userPermission & Permissions.R ) { // 0011 & 0100 結果為0,返回假
console.log('此使用者有讀許可權');
}
可以直接按F12將程式碼貼上過去執行,可以看到瀏覽器最後輸出了“此使用者有可執行許可權“和”此使用者有寫許可權”。
具體是怎麼實現的呢?
我們的Permissions共有3中許可權,而使用者許可權userPermission預設為0,代表無許可權。
在賦權操作中,將userPermission 與Permissions進行或運算,此時userPermission對應位上的0會變成1,即代表使用者擁有了此許可權。
而在鑑權操作中,將userPermission 與Permissions進行與運算,此時若userPermission對應位上的數字為1,則會返回一個大於0的值,也就是真值,代表使用者擁有此許可權;而使用者沒有此許可權時返回的結果就是0了,鑑權為假。
如此,一個簡單的使用者鑑權系統就完成了。
在renderer.ts中作者也向我們展示了具體的使用方式,感興趣的不妨點選連結去看看。