要知道渲染真实DOM的开销是很大的,比如有时候我们修改了某个数据,如果直接渲染到真实 dom 上会引起整个 dom 树的重绘和重排,有没有可能我们只更新我们修改的那一小块 dom 而不要更新整个 dom 呢?
diff算法能够帮助我们
我们先根据真实 DOM 生成一颗Virtual DOM,当Virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的 DOM 上,然后使oldVnode的值为Vnode
diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的 DOM打补丁
VirtualDOM和真实DOM的区别
Virtual DOM是将真实的 DOM 的数据抽取出来,以对象的形式模拟树形结构。比如 dom 是这样的:
html
<div>
<p>123</p>
</div>对应的Virtual DOM(伪代码):
js
var Vnode = {
tag: 'div',
children: [{ tag: 'p', text: '123' }],
}提示
温馨提示:VNode和oldVNode都是对象,一定要记住
- 用 JavaScript 对象模拟 DOM
- 把此虚拟 DOM 转成真实 DOM 并插入页面中
- 如果有事件发生修改了虚拟 DOM
- 比较两棵虚拟 DOM 树的差异,得到差异对象
- 把差异对象应用到真正的 DOM 树上
VNode
对于VNode,相信大家一点都不陌生,用于表示虚拟节点,是实现Virtual DOM的一种方式。那么它究竟是怎样的呢?我们就去Vue源码里探讨一下
ts
export default class VNode {
// ...
constructor(
tag?: string,
data?: VNodeData,
children?: Array<VNode> | null,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag // 标签名
this.data = data // 属性 如id/class
this.children = children // 子节点
this.text = text // 文本内容
this.elm = elm // 该VNode对应的真实节点
this.ns = undefined // 节点的namespace
this.context = context // 该VNode对应实例
this.fnContext = undefined // 函数组件的上下文
this.fnOptions = undefined // 函数组件的配置
this.fnScopeId = undefined // 函数组件的ScopeId
this.key = data && data.key // 节点绑定的key 如v-for
this.componentOptions = componentOptions // 组件VNode的options
this.componentInstance = undefined // 组件的实例
this.parent = undefined // vnode组件的占位符节点
this.raw = false // 是否为平台标签或文本
this.isStatic = false // 静态节点
this.isRootInsert = true // 是否作为根节点插入
this.isComment = false // 是否是注释节点
this.isCloned = false // 是否是克隆节点
this.isOnce = false // 是否是v-noce节点
this.asyncFactory = asyncFactory // 异步工厂方法
this.asyncMeta = undefined // 异步meta
this.isAsyncPlaceholder = false // 是否为异步占位符
}
// 别名
get child(): Component | void {
return this.componentInstance
}
}这里千万不要因为VNode的这么多属性而被吓到,或者咬紧牙去摸清楚每个属性的意义,其实,我们主要了解其几个核心的关键属性就差不多了,例如:
tag属性即这个VNode的标签属性data属性包含了最后渲染成真实 dom 节点后,节点上的class,attribute,style以及绑定的事件children属性是VNode的子节点text属性是文本属性elm属性为这个VNode对应的真实 dom 节点key属性是VNode的标记,在diff过程中可以提高diff的效率
Virtual DOM除了它的数据结构的定义,映射到真实的 DOM 实际上要经历VNode的create、diff、patch等过程