【vue3】巧用位运算,使用一个变量来表示虚拟节点及其children的身份

806 阅读2分钟

概览

最近在学习vue3源码,源码中有一段藏在角落不起眼的代码,细品之后发现其技巧性很强。这不,他就是vue3巧用位运算,使用一个变量来表示虚拟节点及其children的身份。

场景

vue根据虚节点(以下简称:vnode)来渲染其页面,vnode有一个属性shapeFlag表示其身份。在vue3中虚拟节点有很多中种类,如:元素、函数组件、带状态的组件等。vnode同时有一个属性children来表示其孩子,孩子也有很多身份,如:文本节点,数组,插槽等。 vue是怎么通过一个type就能判断虚拟节点本身及其孩子的身份的了?

巧用位运算

使用左移(<<)来表示身份

vue里面有一个描述虚拟节点身份的文件,他的结构是ts的枚举,其中就使用到了左移运算符,具体如下

export const enum ShapeFlags {
  // 元素
  ELEMENT = 1,
  // 函数组件
  FUNCTIONAL_COMPONENT = 1 << 1,
  // 带状态组件
  STATEFUL_COMPONENT = 1 << 2,
  // 描述儿子
  // 文本儿子节点
  TEXT_CHILDREN = 1 << 3,
  // 数组节点
  ARRAY_CHILDREN = 1 << 4,
  // 插槽
  SLOTS_CHILDREN = 1 << 5,
}

可以发现里面采用了位运算,我们用八位二进制数表示如下:

export const enum ShapeFlags {
  // 元素
  ELEMENT = 1 = 0000 0001,
  // 函数组件
  FUNCTIONAL_COMPONENT = 1 << 1 = 2 = 0000 0010,
  // 带状态组件
  STATEFUL_COMPONENT = 1 << 2 = 4 = 0000 0100,
  // 描述儿子
  // 文本儿子节点
  TEXT_CHILDREN = 1 << 3 = 8 = 0000 1000,
  // 数组节点
  ARRAY_CHILDREN = 1 << 4 = 16 = 0001 0000,
  // 插槽
  SLOTS_CHILDREN = 1 << 5,
}

使用或(|)来计算节点身份

以下是一个真实dom:

<p>
    我不想上班
</p>

这个真实dom用vnode描述,其结构可能如下:

const vnode = {
    type: 'p',
    shapeFlag: // ? 这里应该怎么表示其是一个元素,其孩子是一个文本节点
    children: '我不想上班'
}

我们使用或进行以下运算, shapeFla = ELEMENT 或上 TEXT_CHILDREN

vnode.shapeFlag = ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN
// 1 | 8 = 0000 0001 | 0000 1000 = 0000 1001 = 9

最终shapeFlag = 0000 1001 = 9

接下来我们通过一系列的例子来说明,怎么判断身份。

使用与(&)来判断其身份

由上可得一个vnode,表示如下:

const vnode = {
    type: 'p',
    shapeFlag: 9, // 0000 1001
    children: '我不想上班'
}

下面进行一系列的测试:

  1. 判断该节点是不是一个带状态的组件
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)

0000 1001 & 0000 0100 = 0000 0000 = 0 由此可以得出,这是一个错误结论

  1. 判断该节点是不是一个元素节点
if (vnode.shapeFlag & ShapeFlags.ELEMENT)

0000 1001 & 0000 0001 = 0000 0001 = 1 由此可以得出,他是元素节点

  1. 判断该节点的孩子是不是数组
if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN)

0000 1001 & 0001 0000 = 0000 0000 = 0 由此得出,他的孩子不是数组

  1. 判断该节点的孩子是不是文本
if (vnode.shapeFlag & ShapeFlags.TEXT_CHILDREN)

0000 1001 & 0000 1000 = 0000 1000 = 8 由此得出,他的孩子是文本节点

进过测试我们发现,通过与运算,我们可以只使用一个变量,就能判断节点本身和其孩子的身份

总结

总结一下,差不多三步:

  1. 使用左移来定义身份类型
  2. 使用或来组合类型
  3. 使用与来判断类型

好的代码读起来赏心悦目简直就是远方的诗,现实却是在屎山沉沦,但没办法,生活要继续,眼前的苟且要解决!