为什么使用 | & 运算符
在Vue.js源码中,ShapeFlags 是一个标志位枚举,用于表示不同类型的 VNode(虚拟节点)。这些标志位用于在创建和处理 VNode 时快速检查节点的类型和特性。
使用 |(或运算)和 &(与运算)的目的在于将多个标志位组合到一个整数中。这样做有以下好处:
- 紧凑表示: 将多个标志位通过位运算合并到一个整数中,可以用更紧凑的方式表示一个 VNode 的特性。这有助于减小内存占用。
- 快速检查: 通过使用
&运算,你可以快速检查一个 VNode 是否具有某个特定的标志位,而不需要逐个检查每个标志位。
下面是一个简单的示例,说明如何使用 | 和 & 运算符:
const ShapeFlags = {
ELEMENT: 1,
FUNCTIONAL_COMPONENT: 1 << 1,
STATEFUL_COMPONENT: 1 << 2,
TEXT_CHILDREN: 1 << 3,
ARRAY_CHILDREN: 1 << 4,
SLOTS_CHILDREN: 1 << 5
};
// 创建一个具有 ELEMENT 和 TEXT_CHILDREN 标志位的 VNode
const vnode = ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN;
// 检查 VNode 是否具有 TEXT_CHILDREN 标志位
if (vnode & ShapeFlags.TEXT_CHILDREN) {
console.log('具有 TEXT_CHILDREN 标志位');
}
在这个示例中,每个标志位都是通过位移运算得到的。例如,1 << 1 表示将二进制中的 1 左移一位,得到二进制 10,即 2。这样,每个标志位都是不同的 2 的幂,方便在二进制表示中进行组合和检查。
实现 ShapeFlags
// runtime-core/vnode.ts
export function createVNode(type, props?, children?) {
const vnode = {
type,
props,
component: null,
children,
shapeFlag: getShapeFlags(type), // 初步根据 type 判断 shapeFlag
el: null,
key: props?.key,
};
// 针对 children 进一步判断 shapeFlag
if (typeof children === "string") {
// 使用 | 运算符,这样就能兼顾 type 和 children
vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN;
} else if (Array.isArray(children)) {
vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN;
}
// slots 的条件:组件 + children 是 object
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
if (typeof children === "object") {
vnode.shapeFlag |= ShapeFlags.SLOT_CHILDREN;
}
}
return vnode;
}
function getShapeFlags(type) {
return typeof type === "string"
? ShapeFlags.ELEMENT
: ShapeFlags.STATEFUL_COMPONENT;
}
// runtime-core/renderer.ts
function patch(vnode, container) {
const { shapeFlag, type } = vnode;
if (shapeFlag & ShapeFlags.ELEMENT) {
// 处理 Element
processElement(vnode, container);
} else {
// 处理组件
processComponent(vnode, container);
}
}
// shared.ts
export enum ShapeFlags {
ELEMENT = 1,
STATEFUL_COMPONENT = 1 << 1,
TEXT_CHILDREN = 1 << 2,
ARRAY_CHILDREN = 1 << 3,
SLOT_CHILDREN = 1 << 4,
}
// | 运算符逻辑,同时为 0 才是 0,(只要有 1 就是 1)
// & 运算符逻辑,同时为 1 才是 1,(只要有 0 就是 0)