虚拟DOM是什么
React的虚拟DOM
const vNode = {key : null,props : {children : [ //子元素们{type : 'span',...},{type : 'span',...}],className : "red" //标签上的属性onClick : ()=>{} //事件},ref : null,type : "div", //标签名or组件名...}
Vue的虚拟DOM
const vNode = {tag : "div", //标签名or组建名data : {class : "red", //标签上的属性on : {click : ()=>{} //事件}},children : [ //子元素们{tag : "span",...},{tag : "span",...}],...}
创建虚拟DOM
React.createElement
createElement('div',{className:"red",onClick:()=>{}},[createElement('span',{},'span1'),createElement('span',{},'span2')])
Vue(只能在render函数里得到h)
h('div',{class:'red',on:{click:()=>{}},},[h('span',{},'span1'),h('span',{},'span2')])
改进版创建虚拟DOM方法
React
<div className="red" onClick={fn}><span>span1</span><span>span2</span></div>//需要通过babel转为createElement形式
Vue Template
<div class="red" @click="fn"><span>span1</span><span>span2</span></div>//通过vue-loader转为h形式
虚拟DOM的优点
减少DOM操作
虚拟DOM可以讲多次操作合并为一次操作,比如你添加1000个节点,却是一个接一个操作的(减少频率)
虚拟DOM借助DOM diff可以把多余的操作省掉,比如你添加1000个节点,其实只是10个是新增的(减少范围)
跨平台
虚拟DOM不仅可以变成DOM,还可以变成小程序、IOS应用、安卓应用,因为虚拟DOM本质只是一个JS对象
虚拟DOM的缺点
需要额外的创建函数,如createElement或h,但可以用改进版的创建方法
DOM diff是什么
Dom diff是虚拟DOM的对比算法,该算法主要是对比两个虚拟DOM的不同点
举例解释
<div :class="x"><span v-if="y">{string1}</span><span>{string2}</span></div>
图示,把虚拟DOM想象成树形
数据变化,x从red变成green


DOM diff发现
div标签类型没变,只需要更新div对应的DOM属性
子元素没变,不更新
数据变化,y从true变成false


DOM diff发现
div没变,不用更新
子元素1标签没变,但是children变了,更新DOM内容
子元素2不见了,删除对应的DOM
总结:
DOM diff其实是一个函数,我们称之为patch。函数接收两个虚拟DOM对象,然后通过函数内部的逻辑,找出两个虚拟DOM的区别: patches = patch(oldVNode, newVNode) ,而这个patches就是需要更新的DOM操作
DOM diff的逻辑
Tree diff:讲新旧两个DOM逐层对比,找出哪些节点需要更新。如果节点是组件就看Component diff
Component diff:如果节点是组件,就先看组价类型。类型不同直接替换(删除旧的)。类型相同则更新属性。然后深入组件做Tree diff(递归)
Element diff:如果节点是原生标签,则看标签名。标签名不同直接替换,相同则只更新属性。然后进入标签后代做Tree diff(递归)
DOM diff的优点
可以做到只把变化的部分重新渲染,提高渲染效率的过程
DOM diff的问题(key)
DOM diff也会存在有一个问题:同级对比存在bug
解释
回顾上面的 数据变化,y从true变成false 的DOM算法。我们的原意是把第一个span删除,留下第二个span。但是DOM diff算法的操作是发现了第一个span的内容又”hello”变成了”world”,所以把第一个span的内容变成了第二个span的内容,删除第二个。
所以,我们一般会给组件添加上key属性,这个key属性是节点的唯一标识。有了这个标识,在看上面的例子,DOM diff会发现第一个span的key不见了,就删除了第一个span,留下第二个span,完全符合我们的预期
总结:
key值的绑定对象不要使用index
因为当使用index作为key的值,那么在删除第二项的时候,index会从1 2 3变成 1 2(而不是1 3)。因为DOM diff会理解为,2发生了改变,在2的基础上做更新,把3的内容更新到2上,把3删除
