Skip to content

虚拟 Dom

定义

官方文档中的定义非常精炼:

虚拟 Dom 是用一个原生的 JS 对象去描述一个 DOM 节点,不需要包含操作 DOM 的方法,所以它比创建一个 DOM 的代价要小很多。

目的

虚拟dom在不同框架中使用的特点包括

  1. 避免直接操作dom 提高开发效率
  2. 作为一个中间层可以跨平台
  3. 虚拟dom不一定能提高性能
  • 首次渲染的时候会增加开销
  • 复杂视图情况下提升渲染性能

针对Vue来说,因为 Vue 是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作 DOM,而操作真实 DOM 又是非常耗费性能的。最直观的解决思路就是不要盲目的去更新视图,而是通过对比数据变化前后的状态,计算出视图中哪些地方需要更新,只更新需要更新的地方。

实现

vue2.0snabbdom的基础上来实现 虚拟dom,snabbdom源码link[https://github.com/snabbdom/snabbdom] 源码的重点是snabbdom是如何实现虚拟Dom映射到真实的DOM, 核心就是patch过程,至于snabbdom详细的分析,可以看这篇文章👇 虚拟dom与diff算法详解[https://juejin.cn/post/7083007456900546573],

Vue 中的 VNode

在 Vue 中,Virtual DOM 使用 一个 Class 来描述 VNode,定义在 src/core/vdom/vnode.js。

js
export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  devtoolsMeta: ?Object; // used to store functional render context for devtools
  fnScopeId: ?string; // functional scope id support

  constructor(
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child(): Component | void {
    return this.componentInstance
  }
}

从代码中可以看出 VNode 包括的属性有:

  • children 子节点或文本内容
  • data 描述tag,可以设置dom的属性或标签的属性
  • elm 真实dom
  • tag 标签名称或组件对象
  • key 复用元素 ...

这里只是虚拟节点的描述,基于snabbdomVue实现虚拟Dom映射到真实的DOM同样经过了create vnodepatch的过程,核心一致,只是 Vue 加入了一些处理平台相关和 Vue 特性,下一篇将分析实现生成Vnode的函数_createElement()