前段时间有个人问,react中想要循环列表时,为什么要设置key?
如果是在开发中碰到警告, 一看可能就会想到:哦,原来是循环忘记加 key 了,大概原因知道,但是其实还真没仔细想过为什么要加 key。所以查了相关文档,自己简单做下总结。
一。为什么要用key?
react 官方文档是这样描述 key 的:
keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。因此你应当给数组中的每一个子元素赋予一个缺点的标识。
react 的 diff 算法是把 key 当成唯一 id 后对比组件的 value 来确定是否需要更新的,所以说如果没有 key, react 将不会知道如何更新组件。
1.虚拟 DOM 和 diff 算法
React 最为核心的就是 Virtual DOM 和 Diff 算法。React 在内存中维护一颗虚拟 DOM 树,当数据发生变化时(state & props),会自动地更新虚拟 DOM ,获得一个新的虚拟 DOM 树,然后通过 Diff 算法,比较新旧虚拟 DOM 树,找出最小的有变化的部分,将这个变化的部分(patch)加入队列 ,最终批量的更新这些 Patch 到实际的 DOM 中。
React diff 原理(大厂必问)
1.把树形结构按照层级分解,只比较同级元素;
2.给列表结构的每个单元添加唯一的 key 属性,方便比较;
3.React 只会匹配相同的 class 的 component (这里的 class 指的是组件的名字);
4.合并操作,调用 component 的 setState 方法的时候,React 将其标记为 dirty 到每一个事件循环结束。React 检查所有标记 dirty 的 component 重新绘制;
5.选择性子树渲染。
二。使用 index 做 key 存在什么问题?
假设我们在 state 中有一个列表 [a, b, c ],在遍历渲染时用索引作为 key,那么就是这样:
a ---> 0
b ---> 1
c ---> 2
这个时候我们执行一个操作,点击删除数组中的 a,此时我们的数组列表为 [ b, c ],在遍历渲染时仍然会使用索引作为 key,结果如下:
b ---> 0
c ---> 1
看出区别了吗?此时的 key 是唯一的标记每一个列表吗?显然不是。
如果不适用索引做 key,程序额能快速的对比出差异,使用索引也能对比出差异,但是必须对整个 虚拟 DOM 做对比,这样大大消耗了新旧虚拟 DOM 的对比的性能。
三。如何使用正确的 key?
1.纯展示
如果组件单纯的用来展示,不会发生其他变更,那么使用 index 或者其他任何不相同的值做 key 都是可以的,因为不会发生 diff,就不会用到 key。
2.子组件可能发生改变 / 使用了非受控组件
受控组件:
1.每当表单的状态发生变化时,都会被写入到组件的 state 中;
2.在受控组件中,组件渲染出的状态与它的 value 或 checked prop 相对应;
3.react 受控组件更新 state 的流程。
流程:
(1)通过在初始 state 中设置表单的默认值;
(2)每当表单的值发生变化时,调用 onChange 事件处理器;
(3)事件处理器通过合成对象 e 拿到改变后的状态,并更新应用的 state;
(4)setState 触发视图的重新渲染,完成表单组件值的更新。
使用受控组件需要为每一个组件绑定一个 onChange 事件,并且顶一个事件处理器来同步表单值和组件的状态,某些情况可以使用一个事件处理器来处理多个表单域。
受控 onChange 对应的检测属性:
文本框 ---> event.target.value
多选框 ---> event.target.checked
文本域 ---> event.target.value
下拉框 ---> event.target.value
非受控组件:
1.如果一个表单组件没有 value prop 就可以称为非受控组件
2.非受控组件时一种反模式,它的值不受组件自身的 state 或 props 控制
3. 通常需要为其添加ref prop 来访问渲染后的底层的 DOM 元素
4.可通过添加 defaultValue 指定 value 值。
二者的区别:
受控组件的状态由开发者维护,非受控组件的状态由组件自身维护。