ビットによる VNode の表現
VNode の種類をビットで表現する
VNode にはいろんな種類のものがあります。例えば、今実装しているものだと、以下のようなものです。
- component node
- element node
- text node
- 子要素が text かどうか
- 子要素が配列かどうか
そして、これから先はさらにいろんな種類の Vnode が追加実装されることでしょう。
例えば、slot, keep-alive, suspense, teleport などがそうです。
今のところ、type === Text
や typeof 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 を考えます。
const vnode = {
type: 'div',
children: [
{ type: 'p', children: ['hello'] },
{ type: 'p', children: ['hello'] },
],
}
まず、フラグの初期値は 0 です。(簡略化のため 8bit で説明しています。)
let shape = 0b0000_0000
ここで、この VNode は element であり、子要素を配列で持っているので ELEMENT というフラグと ARRAY_CHILDREN というフラグが立ちます。
shape = shape | ShapeFlags.ELEMENT | ELEMENT.ARRAY_CHILDREN // 0x00010001
これにより、shape というただ一つの数値で「element でありかつ、子要素を配列を持っている」という情報を表現できました。
あとはこれを renderer 等の分岐で使用すれば効率的に VNode の種類を管理できます。
if (vnode.shape & ShapeFlags.ELEMENT) {
// vnodeがelementの時の処理
}
今回は、すべての ShapeFlags を実装するわけではないので、練習として以下を実装してみてください。
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を追加し、createVNode などで flag を算出してください。
export interface VNode<HostNode = any> { shapeFlag: number }
- renderer では shape を元に分岐処理を実装する。
です!
なんとこのチャプターの説明は以上です。実際に実装していきましょう !