虚拟 DOM
虚拟 DOM 是什么?
虚拟DOM(Virtual DOM)它是使用javaScript对象来描述真实DOM,虚拟DOM的本质就是javaScript对象,使用javaScript对象来描述DOM的结构,通常含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。程序的各种状态变化首先作用于虚拟DOM,最终映射到真实DOM上
虚拟 DOM 的优点?
- 减少 DOM 操作 (减少频率)
- 虚拟 DOM 可以将多次操作合并为一次操作(减少频率)
- 例一
- 比如我要添加 1000 个节点,却是一个接一个操作的
- 使用虚拟 DOM就可以减少操作一次性添加
- 例一
- 虚拟 DOM 借助 DOM diff 可以把多余的操作省掉(减少范围)
- 例二
- 比如你添加 1000 个节点,其实只有 10 个是新增的(减少范围)
- 例二
- 虚拟 DOM 可以将多次操作合并为一次操作(减少频率)
- 跨平台
- dom树的实现模块 和 js 模块 是分开的,这些跨模块的通讯增加了成本。
- dom 操作引起的浏览器的回流和重绘,使得性能开销巨大。
备注:在 pc 端是没有性能问题的(因为 pc 的计算能力强),但随着移动端的发展,越来越多的网页在智能手机上运行,而手机的性能参差不齐,会有性能问题
虚拟dom的意义
- vdom 的真正意义是为了实现跨平台,服务端渲染(从而诞生了react native等);
- 提供一个性能还算不错 Dom 更新策略;
- vdom 让整个 mvvm 框架灵活了起来。
如何创建虚拟 DOM ?
Vue
只能在 render 函数里得到 h ```javascript h(‘div’, { class: ‘red’, on: { click: () => { } }, }, [h(‘span’,{},’span1’), h(‘span’, {}, ‘span2’])
<a name="IHpMc"></a>### ReactReact.createElement```javascriptcreateElement('div',{className:'red',onClick:()=> {}},[createElement('span', {}, 'span1'),createElement('span', {}, 'span2')])
虚拟 DOM 的样子
Vue
const vNode = {tag: "div", // 标签名 or 组件名data: {class: "red", // 标签上的属性on: {click: () => {} // 事件}},children: [ // 子元素{ tag: "span", ... },{ tag: "span", ... }],...}------------------------------------------------------------------------添加一个 div,他的 class 是red,在 div 中有两个子元素,是span
React
const vNode = {key: null,props: {children: [ // 子元素们{ type: 'span', ... },{ type: 'span', ... }],className: "red" // 标签上的属性onClick: () => {} // 事件},ref: null,type: "div", // 标签名 or 组件名...}------------------------------------------------------------------------添加一个 div,他的 class 是red,在 div 中有两个子元素,是span
如何简化创建虚拟 DOM的书写?
Vue Template
h('div', {class: 'red',on: {click: () => { }},}, [h('span',{},'span1'), h('span', {}, 'span2'])------------------------------------------------------------------------必须通过通过 vue-loader 转为 h 形式
React JSX
<div className="red" onClick="{()=> {}}"><span>span1</span><span>span2</span></div>------------------------------------------------------------------------必须通过 babel 转为 createElement 形式
- js 的插入用时是很快的,只是浏览器渲染的时间,没有那么快,并在渲染的时候会让页面不可交互
规模较小的时候虚拟 DOM 是较快的,但是规模非常大的时候,还是 JS 的使用正常,使用虚拟 DOM 会崩的
把虚拟 DOM 想象成树形 ,当数据变化时(从 red 变成green)
- DOM diff 发现 div 标签类型没变,只需要更新 div 对应的 DOM 的属性
- 子元素没变,不更新

- 把虚拟 DOM 想象成树形 ,当数据变化时(y 从 true 变成 false)
- div 没变,不用更新
- 子元素1标签没变,但是children变了,更新 DOM 内容
- 子元素2不见了,删除对应的 DOM
diff-虚拟 DOM 的对比算法
- 就是一个函数,称之为patch
patches = patch(oldVNode, newVNode)- oldVNode旧虚拟节点
- newVNode新虚拟节点
- 两个节点进行对比
patches 就是要运行的 DOM 操作,可能长这样(伪代码)
[{type: 'INSERT', vNode: ... },{type: 'TEXT', vNode: ... },{type: 'PROPS', propsPatch: [...]}]
DOM diff 的基本逻辑
Component diff (组件)
如果节点是组件,就先看组件类型
- 类型不同直接替换(删除旧的)
- 类型相同则只更新属性
-
Element diff (标签)
如果节点是原生标签,则看标签名
- 标签名不同直接替换,相同则只更新属性
-
Tree diff
将新旧两棵树逐层对比,找出哪些节点需要更新
- 如果节点是组件就看 Component diff
-
DOM diff 的优点?
DOM diff 的问题(key)
不使用 key,或者使用 index 作为可以会有 bug
- 如果你用 index作为key,那么在删除第二项的时候, index就会从1,2,3变成1,2(因为 index永远都是连续的,所以不可能是1,3),那么ue依然会认为你删除的是第三项。
- 会使第三项消失
- 永远不要使用 index 作为 key
- 修改 bug
- 创建 id,使用 id 作为 key
- 这样DOM diff 在对比的时候更加准确,通过对比删除自己想要的东西
- 更加精准
