虚拟 DOM
众所周知,虚拟 DOM 是框架对原生 DOM 操作的优化方法
在 Vue 中,我们一般这样创建节点
<div class='red' @click="onClick"></div>
React 则使用 JSX 语法来创建,但是本质上都是创建了一个对象,而不是我们所想的原生标签的样子,我们成为 VNode 对象
以 React 的虚拟 DOM 为例子
const VNode={
props:{
children:[
{type:'span',...}
{type:'span',...}
],
className:'wrapper',
onClick:()=>{}
},
ref:null,
type:'div'
}
看吧,它长得并不像真实 DOM,只是我们可以依靠一些属性来判断它的信息
知道虚拟 DOM 的样子了,那么它到底有什么优点和缺点呢?
- 可以减少 DOM 操作
如果我们用原生 api 来添加很多节点,会拉低渲染的效率,虽然代码运行很快,但是可能需要等待,而虚拟 DOM 是批量合并 DOM 操作,一次性部署
其独有的 DOM diff 可以判断新旧虚拟 DOM 的变化,将渲染任务大大压缩,比如只是内容的变化,就只需要替换内容而不是新建节点
- 可以跨平台 虚拟 DOM 是一个对象,它可以变成小程序,或者安卓和 ios 的原生组件
缺点就是需要额外的创建函数,即 createElement 和 h
DOM diff
那么 diff 是如何决定节点该如何更新呢
进行同级对比,如果节点为原生标签,先看标签名,若标签名不同,则直接替换,如果相同,则更新其属性
如果节点为组件,先观察组件类型,若不同也直接替换,类型相同也只更新属性
但是即使是如此细致的对比,也会出现问题
我们都知道,在循环一个数组来创建节点时,需要 key 的存在
我曾做过实验,创建一些含有复选框的子元素,再写一个添加元素的功能
比如你先选中第一个元素,然后在数组的首位新增一个元素,此时,新元素的复选框被选中,而原来的元素的复选框则没有被选中,看起来就好像是新元素继承了原来元素的状态
这是因为没有加 key,diff 无法精确检测新旧对象的不同,所以新元素直接复用了原来元素的状态
所以,key 的作用就是,在我们更新了渲染列表后,能够让 DOM diff 精确判断新旧虚拟 DOM 的不同,从而实现精确渲染
最后要记得,key 最好是 id,而不是数组的下标,因为数组下标不唯一,可能会改变