Skip to content

ビットによる VNode の表現

VNode の種類をビットで表現する

VNode にはいろんな種類のものがあります。例えば、今実装しているものだと、以下のようなものです。

  • component node
  • element node
  • text node
  • 子要素が text かどうか
  • 子要素が配列かどうか

そして、これから先はさらにいろんな種類の Vnode が追加実装されることでしょう。
例えば、slot, keep-alive, suspense, teleport などがそうです。

今のところ、type === Texttypeof type === "string", typeof type === "object" などで分岐をおこなっています。

これらをいちいち判定するのは非効率ですし、本家の実装に倣ってビットで表現することにしてみましょう。
Vue ではこれらのビットは ShapeFlags と呼ばれています。その名の通り、VNode の Shape を表すものです。
(厳密には Vue ではこの ShapeFlags と Text や Fragment などの Symbol を使って VNode の種類を判別しています)
https://github.com/vuejs/core/blob/main/packages/shared/src/shapeFlags.ts

ビットフラグがどのようなものかというと、数値の各ビットを特定のフラグとしてみなすというものです。

例として以下のような VNode を考えます。

ts
const vnode = {
  type: 'div',
  children: [
    { type: 'p', children: ['hello'] },
    { type: 'p', children: ['hello'] },
  ],
}

まず、フラグの初期値は 0 です。(簡略化のため 8bit で説明しています。)

ts
let shape = 0b0000_0000

ここで、この VNode は element であり、子要素を配列で持っているので ELEMENT というフラグと ARRAY_CHILDREN というフラグが立ちます。

ts
shape = shape | ShapeFlags.ELEMENT | ELEMENT.ARRAY_CHILDREN // 0x00010001

これにより、shape というただ一つの数値で「element でありかつ、子要素を配列を持っている」という情報を表現できました。
あとはこれを renderer 等の分岐で使用すれば効率的に VNode の種類を管理できます。

ts
if (vnode.shape & ShapeFlags.ELEMENT) {
  // vnodeがelementの時の処理
}

今回は、すべての ShapeFlags を実装するわけではないので、練習として以下を実装してみてください。

ts
export const enum ShapeFlags {
  ELEMENT = 1,
  COMPONENT = 1 << 2,
  TEXT_CHILDREN = 1 << 3,
  ARRAY_CHILDREN = 1 << 4,
}

やることとしては、

  • shared/shapeFlags.ts にフラグを定義
  • runtime-core/vnode.ts で vnode に shape を定義
    ts
    export interface VNode<HostNode = any> {
      shapeFlag: number
    }
    を追加し、createVNode などで flag を算出してください。
  • renderer では shape を元に分岐処理を実装する。

です!

なんとこのチャプターの説明は以上です。実際に実装していきましょう !

Released under the MIT License.