Virtual DOM

Virtual DOM 这个概念相信大部分人都不会陌生,它产生的前提是浏览器中的 DOM 是很“昂贵"的,为了更直观的感受,我们可以简单的把一个简单的 div 元素的属性都打印出来,如图所示:
Virtual DOM - 图1
可以看到,真正的 DOM 元素是非常庞大的,因为浏览器的标准就把 DOM 设计的非常复杂。当我们频繁的去做 DOM 更新,会产生一定的性能问题。

而 Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。在 Vue.js 中,Virtual DOM 是用 VNode 这么一个 Class 去描述,它是定义在 src/core/vdom/vnode.js 中的。

  1. export default class VNode {
  2. tag: string | void;
  3. data: VNodeData | void;
  4. children: ?Array<VNode>;
  5. text: string | void;
  6. elm: Node | void;
  7. ns: string | void;
  8. context: Component | void; // rendered in this component's scope
  9. key: string | number | void;
  10. componentOptions: VNodeComponentOptions | void;
  11. componentInstance: Component | void; // component instance
  12. parent: VNode | void; // component placeholder node
  13. // strictly internal
  14. raw: boolean; // contains raw HTML? (server only)
  15. isStatic: boolean; // hoisted static node
  16. isRootInsert: boolean; // necessary for enter transition check
  17. isComment: boolean; // empty comment placeholder?
  18. isCloned: boolean; // is a cloned node?
  19. isOnce: boolean; // is a v-once node?
  20. asyncFactory: Function | void; // async component factory function
  21. asyncMeta: Object | void;
  22. isAsyncPlaceholder: boolean;
  23. ssrContext: Object | void;
  24. fnContext: Component | void; // real context vm for functional nodes
  25. fnOptions: ?ComponentOptions; // for SSR caching
  26. fnScopeId: ?string; // functional scope id support
  27. constructor (
  28. tag?: string,
  29. data?: VNodeData,
  30. children?: ?Array<VNode>,
  31. text?: string,
  32. elm?: Node,
  33. context?: Component,
  34. componentOptions?: VNodeComponentOptions,
  35. asyncFactory?: Function
  36. ) {
  37. this.tag = tag
  38. this.data = data
  39. this.children = children
  40. this.text = text
  41. this.elm = elm
  42. this.ns = undefined
  43. this.context = context
  44. this.fnContext = undefined
  45. this.fnOptions = undefined
  46. this.fnScopeId = undefined
  47. this.key = data && data.key
  48. this.componentOptions = componentOptions
  49. this.componentInstance = undefined
  50. this.parent = undefined
  51. this.raw = false
  52. this.isStatic = false
  53. this.isRootInsert = true
  54. this.isComment = false
  55. this.isCloned = false
  56. this.isOnce = false
  57. this.asyncFactory = asyncFactory
  58. this.asyncMeta = undefined
  59. this.isAsyncPlaceholder = false
  60. }
  61. // DEPRECATED: alias for componentInstance for backwards compat.
  62. /* istanbul ignore next */
  63. get child (): Component | void {
  64. return this.componentInstance
  65. }
  66. }

可以看到 Vue.js 中的 Virtual DOM 的定义还是略微复杂一些的,因为它这里包含了很多 Vue.js 的特性。这里千万不要被这些茫茫多的属性吓到,实际上 Vue.js 中 Virtual DOM 是借鉴了一个开源库 snabbdom 的实现,然后加入了一些 Vue.js 特色的东西。我建议大家如果想深入了解 Vue.js 的 Virtual DOM 前不妨先阅读这个库的源码,因为它更加简单和纯粹。

总结

其实 VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。

Virtual DOM 除了它的数据结构的定义,映射到真实的 DOM 实际上要经历 VNode 的 create、diff、patch 等过程。那么在 Vue.js 中,VNode 的 create 是通过之前提到的 createElement 方法创建的,我们接下来分析这部分的实现。

原文: https://ustbhuangyi.github.io/vue-analysis/data-driven/virtual-dom.html