⚡ 30 秒速记

  • VNode 描述类型、属性、子节点与身份
  • 元素、文本、组件和片段需要不同信息
  • 类型标志方便分派,不是业务必需字段
  • 宿主节点引用连接描述与实际结果
  • 键负责身份,不能替代内容更新

设计虚拟节点,就是设计一份渲染器能看懂的界面数据协议。 元素要记录标签和属性,文本要保存内容,组件则要指向组件定义与输入,片段还涉及多个宿主节点。类型标志可以让更新路径快速分派,实际节点引用帮助复用已经创建的结果。字段名和位运算方式可以变化,核心是信息足够、语义清楚,并且能覆盖挂载、更新和卸载。

版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。

上一章讲述了组件的本质,知道了一个组件的产出是 VNode,渲染器(Renderer)的渲染目标也是 VNode。可见 VNode 在框架设计的整个环节中都非常重要,甚至设计 VNode 本身就是在设计框架,VNode 的设计还会对后续算法的性能产生影响。本章我们就着手对 VNode 进行一定的设计,尝试用 VNode 描述各类渲染内容。

# 用 VNode 描述真实 DOM

一个 html 标签有它的名字、属性、事件、样式、子节点等诸多信息,这些内容都需要在 VNode 中体现,我们可以用如下对象来描述一个红色背景的正方形 div 元素:

const elementVNode = {
  tag: 'div',
  data: {
    style: {
      width: '100px',
      height: '100px',
      backgroundColor: 'red'
    }
  }
}

我们使用 tag 属性来存储标签的名字,用 data 属性来存储该标签的附加信息,比如 style、class、事件等,通常我们把一个 VNode 对象的 data 属性称为 VNodeData。

为了描述子节点,我们需要给 VNode 对象添加 children 属性,如下 VNode 对象用来描述一个有子节点的 div 元素:

const elementVNode = {
  tag: 'div',
  data: null,
  children: {
    tag: 'span',
    data: null
  }
}

若有多个子节点,则可以把 children 属性设计为一个数组:

const elementVNode = {
  tag: 'div',
  data: null,
  children: [
    {
      tag: 'h1',
      data: null
    },
    {
      tag: 'p',
      data: null
    }
  ]
}

除了标签元素之外,DOM 中还有文本节点,我们可以用如下 VNode 对象来描述一个文本节点:

webapp
公众号
开发者导航
切换夜间模式
点击侧边栏上一篇
点击侧边栏下一篇
折叠侧边栏
收起全部