手撸mini-vue之shapeFlags

623 阅读1分钟

shapeFlag用于描述当前虚拟节点vnode的类型,在目前的实现代码中有两处需要用到shapeFlag进行判断

  • renderer.tspatch方法中判断vnodeElement``Compnent
  • renderer.tsmountElement方法中判断vnodechildren类型是string还是array

正常实现

const shapeFlags = {
  ELEMENT: 0,
  STATEFUL_COMPONENT: 0,
  TEXT_CHILDREN: 0,
  ARRAY_CHILDREN: 0
}

以上根据vnode的类型和vnode.children的类型设置shapeFlags的property

// vnode 的类型是 element
shapeFlags.ELEMENT = 1

// vnode 的类型是 component
shapeFlags.COMPONENT = 1

// vnode.children 的类型是 string
shapeFlags.TEXT_CHILDREN = 1

// vnode.children 的类型是 array
shapeFlags.ARRAY_CHILDREN = 1

设置完shapeFlags的值之后,就可以这么判断

if(shapeFlags.ELEMENT) {
  // vnode 的类型是 element 的情况需要进行的操作
  ...
}
if(shapeFlags.COMPONENT) {
  // vnode 的类型是 component 的情况需要进行的操作
  ...
}
if(shapeFlags.TEXT_CHILDREN) {
  // vnode.children 的类型是 string 的情况需要进行的操作
  ...
}
if(shapeFlags.TEXT_CHILDREN) {
  // vnode.children 的类型是 array 的情况需要进行的操作
  ...
}

这样的实现代码可读性很高,很容易一眼就看出来什么情况下走什么判断

位运算实现

enum ShapeFlags {
  ELEMENT = 1, // 0001
  STATEFUL_COMPONENT = 1 << 1, // 0010
  TEXT_CHILDREN = 1 << 2, // 0100
  ARRAY_CHILDREN = 1 << 3, // 1000
}
// vnode 为 element 类型,vnode.children 为 string 类型
vnode.shapeFlag === 0101

// vnode 为 element 类型,vnode.children 为 array 类型
vnode.shapeFlag === 1001

// vnode 为 component 类型,vnode.children 为 string 类型
vnode.shapeFlag === 0110

// vnode 为 component 类型,vnode.children 为 array 类型
vnode.shapeFlag === 1010

根据上面的规则,实现代码

// vnode.ts
export function vnode(type, props?, children?) {
  const vnode = {
    ...,
    shapeFlag: getShapeFlage(type)
  };
  
  if (typeof children === "string") {
    vnode.shapeFlag |= ShapeFlags.TEXT_CHILDREN;
  } else if (Array.isArray(children)) {
    vnode.shapeFlag |= ShapeFlags.ARRAY_CHILDREN;
  }

  return vnode;
}

function getShapeFlage(type) {
  return typeof type === "string"
    ? ShapeFlags.ELEMENT
    : ShapeFlags.STATEFUL_COMPONENT;
}
// renderer.ts
function patch(vnode, container) {
  const { shapeFlag } = vnode;
  
  if (shapeFlag & ShapeFlags.ELEMENT) {
    processElement(vnode, container);
  } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
    processComponent(vnode, container);
  }
}

function mountElement(vnode: any, container: any) {
  const el = (vnode.el = document.createElement(vnode.type));
  const { children, shapeFlag } = vnode;
  
  if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
    el.textContent = children;
  } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
    mountChildren(vnode, el);
  }

  ...
}

性能: 位运算实现 > 对象实现
代码可读性: 位运算实现 < 对象实现
我认为在实际的开发中,首先保证功能的实现,然后是代码的可读性,最后才从性能方面去进行代码优化