绪论
相信在面试前端岗位的小伙伴肯定遇到过问关于 diff
算法。其实很简单,vue
和react
的 diff
算法的比对过程都是差不到的。话不多说,直接开干。
比对过程
将新的虚拟dom与旧的虚拟dom一个个对比;
如果对比的新虚拟dom与旧的虚拟dom的key不相同时,会创建一个新的真实dom进来;
如果对比的新虚拟dom与旧的有相同的key值时,会再对两个虚拟dom进一步比对,有结构或内容相同的地方会进行一个真实dom的复用,不同的地方会替换或者创建。
key值的重要性
使用正确key
的优点: 完美的遵循了diff算法,使得多数dom得到复用,减少了不必要的dom更新。
不使用正确的key
会造成的问题:造成多处不必要的dom更新,如果存在表单时,可能还会导致界面问题。
示例说明
错误使用key
值带来的问题以react
为例:
import {PureComponent} from 'react'
export default class App extends PureComponent {
state={
list:[
{id:'a',name:'a'},
{id:'b',name:'b'},
{id:'c',name:'c'},
]
}
add=()=>{
this.setState({list:[{id:'d',name:'d'},...this.state.list]})
}
render() {
return (
<div className="App">
<ul >
{
this.state.list.map((v,i)=>{
//当使用 索引 作为key时,造成界面问题
return <li key={i} style={{margin:'10px'}}>
{v.name}:<input type="text" />
</li>
//使用 id 就解决了这个问题
// return <li key={v.id} style={{margin:'10px'}}>
// {v.name}:<input type="text" />
// </li>
})
}
</ul>
<button onClick={this.add}>打乱顺序添加一个人</button>
</div>
)
}
}
运行结果:
现在在每个输入框输入内容:
下面看使用不同key
值的差别
1.使用索引作为key
值,点击按钮后:
可以看到,a
输入框 的内容跑到d
输入框中了,这显然不是我们想要的结果。有些小伙伴可能会疑惑,为什么会这样呢,下面我就用上面说的diff
算法的比对过程来解释这种现象:当我们点击按钮在列表的前面插入了一个item
后,使得组件重新渲染,由于这里是列表渲染,react
底层会使用diff
算法来比对。首先比对第一项,发现key
值相同,然后比对这一项新旧virtual dom
的结构和内容,相同的地方复用,不同的地方替换或新增,对比后发现表单前面的文字不一样,就使用d:
替换了a:
,再对比到表单,发现一样直接复用。这里可能有小伙伴疑惑为什么表单是一样的?里面的内容不是不一样吗?这里确实带有点迷惑,不过也很好解释,因为对比的是virtual dom
,而用户在表单输入的内容是不会呈现在virtual dom
中,可能是因为virtual dom
记录的是html prop
而不是dom prop
,因此此处对比是一样的。这种复用也就造成了界面的问题。其他item
的比对过程以此类推。
2.使用 id
作为key
值,点击按钮后:
可以看到.使用 id
作为key
值是没有问题的。现在我使用上面说的diff
算法的比对过程来说说这里的比对过程。首先对比第一项的key
值,发现不同,直接创建新的dom
进来,再对比后面的发现key
值都一样,直接全都复用。
总结
可以发现正确的使用key
值不仅可以避免出现界面的问题,还减少了react
内部的比对步骤,还提高了dom
复用性。
结尾
相信看完本篇文章底层小伙伴会发现diff
算法也就这么回事。感谢大家阅读本篇文章,如果有问题欢迎提出和分享。