文章目錄
- 前言
- ? 一、基礎知識:什么是二進制?
- ? 二、位運算的基本操作
- ? 三、左移運算 `<<`
- ? 四、實際用途:如何用于狀態標記(PatchFlags)
- ? 五、組合多個狀態標記
- ? 六、小結口訣(記憶)
- ? 一、語法含義
- ? 二、在 `PatchFlags` 中的作用
- 示例說明:
- ? 三、這樣寫的好處
- 1. **便于使用位運算組合多個標記**
- 2. **檢查時快速**
- 3. **性能極高**
- ? 四、實際示例
- ? 五、小結一句話(適合面試)
- 補充
- ? 1. **權限管理(權限位)**
- ? 2. **日志級別控制(Logging Flags)**
- ? 3. **狀態管理(多個狀態疊加)**
- ? 4. **DOM 元素樣式/行為控制**
- ? 5. **游戲開發中的狀態控制**
- 🧠 小結口訣(記憶):
前言
vue3在編譯相關的源碼中:有這一段
export enum PatchFlags {/*** Indicates an element with dynamic textContent (children fast path)*/TEXT = 1,/*** Indicates an element with dynamic class binding.*/CLASS = 1 << 1,/*** Indicates an element with dynamic style* The compiler pre-compiles static string styles into static objects* + detects and hoists inline static objects* e.g. style="color: red" and :style="{ color: 'red' }" both get hoisted* as:*
js* const style = { color: 'red' }* render() { return e('div', { style }) }**/STYLE = 1 << 2,
// 省略其他
這樣用有什么好處?我們先從最基本的二進制和位運算概念講起,再理解 1 << n
的用法,最后回到 PatchFlags
。
? 一、基礎知識:什么是二進制?
計算機底層只認識 0 和 1,我們用“二進制”來表示:
十進制 | 二進制 |
---|---|
1 | 0001 |
2 | 0010 |
4 | 0100 |
8 | 1000 |
? 二、位運算的基本操作
運算符 | 含義 | 示例(十進制) | 示例(二進制) | 結果 | |
---|---|---|---|---|---|
& | 按位與 | 5 & 3 | 0101 & 0011 = 0001 | 1 | |
` | ` | 按位或 | 5 | 3 | 0101 | 0011 = 0111 | 7 |
<< | 左移 | 1 << 2 | 0001 → 0100 | 4 | |
>> | 右移 | 4 >> 1 | 0100 → 0010 | 2 |
? 三、左移運算 <<
左移就是把二進制整體向左“推”一位,后面補 0。
1 => 二進制是 0001
1 << 1 => 是 0010 => 十進制 2
1 << 2 => 是 0100 => 十進制 4
1 << 3 => 是 1000 => 十進制 8
所以可以理解成:
1 << n === 2 的 n 次方
? 四、實際用途:如何用于狀態標記(PatchFlags)
Vue 3 中通過這個方式給每種類型的“變更”分配唯一標志位:
export enum PatchFlags {TEXT = 1, // 0001 => 1CLASS = 1 << 1, // 0010 => 2STYLE = 1 << 2, // 0100 => 4PROPS = 1 << 3, // 1000 => 8
}
每個標志只占一位,組合時不會沖突。
? 五、組合多個狀態標記
// 組合標記
const patchFlag = PatchFlags.CLASS | PatchFlags.STYLE // 2 | 4 = 6 => 0110// 檢查是否包含 PatchFlags.STYLE
if (patchFlag & PatchFlags.STYLE) {console.log('需要更新 style')
}
因為:
patchFlag = 0110
STYLE = 0100
& = 0100 → 非 0,說明包含
? 六、小結口訣(記憶)
技術點 | 口訣 | |
---|---|---|
1 << n | 就是 2 的 n 次方,用來生成唯一的二進制標志位 | |
` | ` 或運算 | 多個標志合并為一個 patchFlag |
& 與運算 | 判斷某個標志是否存在于 patchFlag 中(非 0 表示存在) |
這是 TypeScript(或 JavaScript)中的位運算符 <<
,表示左移位運算(left shift)。
? 一、語法含義
1 << 1 // 結果是 2
1 << 2 // 結果是 4
1 << 3 // 結果是 8
換句話說:
1 << n === 2?
? 二、在 PatchFlags
中的作用
Vue 3 的編譯器使用 PatchFlags
來標記 虛擬 DOM 節點有哪些動態變化的部分。
示例說明:
export enum PatchFlags {TEXT = 1, // 0001CLASS = 1 << 1, // 0010 => 2STYLE = 1 << 2, // 0100 => 4PROPS = 1 << 3, // 1000 => 8// 依此類推...
}
? 三、這樣寫的好處
1. 便于使用位運算組合多個標記
-
可以用按位或(
|
)組合多個 patch 標志:const patchFlag = PatchFlags.TEXT | PatchFlags.CLASS // 即 0001 | 0010 = 0011 => patchFlag = 3
2. 檢查時快速
-
Vue 更新虛擬 DOM 時可以通過按位與(
&
)檢查:if (patchFlag & PatchFlags.CLASS) {// 有 CLASS 需要處理 }
3. 性能極高
- 位運算是底層最輕量的運算方式之一,比數組查找或對象比較快很多。
- 避免使用多個 boolean 類型判斷。
? 四、實際示例
const patchFlag = PatchFlags.CLASS | PatchFlags.STYLE; // 2 | 4 = 6if (patchFlag & PatchFlags.CLASS) {console.log('需要更新 class')
}if (patchFlag & PatchFlags.STYLE) {console.log('需要更新 style')
}
? 五、小結一句話(適合面試)
Vue 3 使用位移表達式(如
1 << n
)生成 2 的冪,實現 PatchFlags 的唯一性標識、位掩碼組合、按位判斷等優化邏輯,在虛擬 DOM diff 過程中既高效又靈活。
補充
非常好!這種“位運算 + 標志位”的方式是一種經典的性能優化技巧,不僅用于 Vue 的 PatchFlags,還廣泛應用于許多實際場景中,比如權限控制、狀態管理、日志等級控制等。
下面我列出幾個常見 應用場景 并配上示例說明:
? 1. 權限管理(權限位)
場景:一個用戶可能擁有多個權限,如讀、寫、刪、改等。
// 權限定義(每個權限占一位)
export enum Permission {READ = 1 << 0, // 0001 = 1WRITE = 1 << 1, // 0010 = 2DELETE = 1 << 2, // 0100 = 4UPDATE = 1 << 3 // 1000 = 8
}// 設置權限(讀 + 寫)
let userPermission = Permission.READ | Permission.WRITE // 0011 = 3// 檢查是否有寫權限
if (userPermission & Permission.WRITE) {console.log('有寫權限')
}
優點:節省空間(用一個整數存多個權限),判斷權限快(位運算)。
? 2. 日志級別控制(Logging Flags)
enum LogLevel {ERROR = 1 << 0,WARN = 1 << 1,INFO = 1 << 2,DEBUG = 1 << 3
}// 當前啟用的日志級別(錯誤 + 警告)
const activeLogLevel = LogLevel.ERROR | LogLevel.WARNfunction log(msg: string, level: LogLevel) {if (activeLogLevel & level) {console.log(`[${LogLevel[level]}]`, msg)}
}log('Something wrong', LogLevel.ERROR) // 會輸出
log('Just info', LogLevel.INFO) // 不會輸出
? 3. 狀態管理(多個狀態疊加)
場景:表示一個任務的多個狀態(如:初始化、加載中、完成、失敗)
enum TaskStatus {INIT = 1 << 0,LOADING = 1 << 1,SUCCESS = 1 << 2,FAILED = 1 << 3
}let currentStatus = TaskStatus.INIT | TaskStatus.LOADING// 判斷是否正在加載
if (currentStatus & TaskStatus.LOADING) {console.log('任務加載中')
}
? 4. DOM 元素樣式/行為控制
例如用于判斷是否啟用多個 UI 特效:
enum UIFlags {HIGHLIGHT = 1 << 0,BLINK = 1 << 1,SHADOW = 1 << 2
}let elementFlags = UIFlags.HIGHLIGHT | UIFlags.SHADOWif (elementFlags & UIFlags.HIGHLIGHT) {// 給元素加高亮
}
? 5. 游戲開發中的狀態控制
例如角色可能處于多種狀態:跳躍、攻擊、被擊中等。
enum PlayerState {IDLE = 1 << 0,JUMPING = 1 << 1,ATTACK = 1 << 2,HURT = 1 << 3
}let currentState = PlayerState.JUMPING | PlayerState.ATTACK// 檢查是否正在攻擊
if (currentState & PlayerState.ATTACK) {console.log('攻擊中')
}
🧠 小結口訣(記憶):
“一個數,多個狀態;左移位,值唯一;按位或,組合用;按位與,判斷快。”