本章内容
目录
- 一、了解 Diff 算法
- 二、key 值的重要性
- 三、为什么不建议使用 index 做 key 值
上一节我们初步了解了 React
中的”虚拟 DOM“ ,本节我们来说一说”虚拟DOM“中的”Diff算法“
一、了解 Diff 算法
在上一篇中,我们有讲到:当
state
或者props
数据变化时会生成新的 ”虚拟DOM“,然后”旧虚拟DOM“和”新虚拟DOM“进行比对。那么怎么进行比对呢?答案是”使用 Diff 算法“。
- ”Diff算法“:我们把两个 ”JS对象“比对的算法叫做 ”Diff 算法“
问:1、”虚拟DOM“ 什么时候被比对
答:当”数据“发生变化的时候,新旧”虚拟DOM“才会进行比较
问2、那什么时候”数据“会发生变化
答:state 或者 props 改变时(代码中使用了 setState() 时,数据发生变化)
问3、为什么 React 的 setState() 设计成”异步“呢
答:为了提高 React 底层的性能。比如说如果我们在间断很短的时间内调用 setState() ,
如果设计成 ”同步“,那么就有三次更新比对”虚拟DOM“的过程,
如果设计成”异步“,就可以合并成”1次“,只做一次”虚拟DOM“的比对,然后去更新一次DOM,避免了性能的消耗
问4、新旧”虚拟DOM“如何进行比对呢
答:采用”同层比较“的方式。首先从最顶层开始,如果一致,就会去比较第二层,以此类推。
如果顶层比较时,不一致,则会直接将”原始DOM“进行全部替换。
这样”比较“和”替换“的暴力方式,看着好像挺浪费性能的(一层不一致就全部替换,很多DOM都没被复用),但由于”同层比较“的算法简单,因此比较的速度很快,性能一下就被提升了
二、key 值的重要性
在之前的案例中,我们循环渲染列表时,会在循环项上廷加一个
key
。那为什么或者有必要添加这个key
呢?答案是:有必要!!!因为在”虚拟DOM“的”Diff算法“中,这个key
为循环的每一项添加了一个卫衣标识,可以有效提高”虚拟DOM“的比对性能
-
假设我们现在有个数组,里面有 5 个数据项。当界面第一次循环渲染时,这5个数据会被映射成 5个”虚拟DOM“节点,生成一个小的”原始虚拟 DOM树“
-
当数据变化时,会生成一个”新的虚拟DOM“
-
然后”新旧虚拟 DOM“进行比对(理想状态)
-
如果此时每一项数据都没有设置
key
值,那么节点和节点的关闭就不确定,需要一个一个的去比较。相反,如果设置了key
值,那么每一项被唯一标识。这样我们就可以将”相同key标识的项“去比对,这无疑加快了比较速度
三、为什么不建议使用 index 做 key 值
指的注意的是,上面提升”性能“的关键点是”同样的节点取了相同的名字“,如果说使用
index
作为key
值,我们不能保证相同的节点拥有同样的名字
- 打开我们的
TodoList
案例,在列表循环时,使用index
作为key
值,然后运行打开浏览器
// TodoList.js 文件
import React, { Component, Fragment } from "react";
import TodoItem from "./TodoItem";
class TodoList extends Component{
constructor(props) {
super(props)
this.deleteData = this.deleteData.bind(this)
this.addListData = this.addListData.bind(this)
this.changeInputValue = this.changeInputValue.bind(this)
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
请输入要进行的事项:
<input value={this.state.inputValue} onChange={this.changeInputValue} />
<button onClick={this.addListData}> 提交 </button>
</div>
<ul> {this.getTodoItem()} </ul>
</Fragment>
)
}
getTodoItem() {
return this.state.list.map((item, index) => {
// 1、使用 index 作为 key 值
return <TodoItem key={index} content={item} index={index} deleteFn={this.deleteData}></TodoItem>
})
}
deleteData(index) {
this.setState((prevState) => {
const list = [...prevState.list]
list.splice(index, 1)
return {list}
})
}
addListData() {
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}))
}
changeInputValue(e) {
const value = e.target.value
this.setState(() => ({inputValue: value}))
}
}
export default TodoList
- 在输入框中输入一些内容,观察界面效果
此时:
Oli --> key: 0
qdywxs --> key: 1
Oli and qdywxs --> key: 2
- 删除 Oli后
此时:
qdywxs --> key: 0
Oli and qdywxs --> key: 1
-
所以说,使用
index
作为key
值,这就造成了同一节点,前后key
值不一样,那么这两个节点就无法建立联系,也就起不到根据key 值快速比对内容的效果了 -
假如我们使用唯一的
key
值,节点的标识是稳定的,也就在比对过程中key
有了重要作用
到此,本章内容结束!