文章目录
- 前言
- ✅ 一、基础知识:什么是二进制?
- ✅ 二、位运算的基本操作
- ✅ 三、左移运算 `<<`
- ✅ 四、实际用途:如何用于状态标记(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('攻击中')
}
🧠 小结口诀(记忆):
“一个数,多个状态;左移位,值唯一;按位或,组合用;按位与,判断快。”