diff 算法
1.先会对新旧dom 进行也给类型比较,如果类型不同就直接替换;
2.如果类型相同就会进行一个原地复用,进行打补丁操作(如更新 className 和标签下的文本内容)
这样会导致一个问题,如果同级多个节点,只是位置发生了变化,但因为相同索引位置对不上,又发现不能复用,就要销毁一棵树并创建一棵新树,实在是太过于低效了。
于是 React 给开发者提供 key 来标记节点,来优化 React diff 算法,告知 React 某个节点其实没有被移除或不能被原地复用,只是换了位置而已,让 React 更新一下位置。
为什么需要 key?
在回答这个问题之前,我们先简单了解一下 React 的 DOM Diff 算法原理。
React 会在状态发生变化时,对真实 DOM 树按需批量更新,产生新的 UI。
为此底层做的工作是:将新旧两棵虚拟 DOM 树进行 diff 对比,计算出 patch 补丁,打到真实 DOM 树上。
为了高效,React 的 diff 算法做了限制:
只做同层级的节点对比,不跨层级比较;
如果元素的类型不同(如从 p 变成 div),那它们就是不相同的,会销毁整个旧子树,并调用其下组件的卸载钩子,然后再创建全新的树,相当消耗性能。
如果类型相同,会进行打补丁操作(如更新 className 和标签下的文本内容)
但这样做会有一个问题,如果同级的多节点 只是位置发生了变化,但因为相同索引位置对不上,又发现不能复用,就要销毁一棵树并创建一棵新树,实在是太过于低效了。
于是 React 给开发者提供 key 来标记节点,来优化 React diff 算法,告知 React 某个节点其实没有被移除或不能被原地复用,只是换了位置而已,让 React 更新一下位置。
列表渲染不提供 key 会怎样?
不提供 key,React 就无法确定某个节点是否移动了。
React 就只会对比相同位置的两个节点,如果它们类型相同(比如都是 li 元素),就会对比 props 的不同,进行 props 的打补丁。
因为 列表渲染通常都是相同的类型,所以位置变动时,多半是会触发节点原地复用效果,倒是不用担心树的销毁重建发生。
原地复用在不提供 key 的时候有时候也是能正确渲染的。
除了一种情况,就是 这个节点有自己的内部状态,最经典的莫过于输入框。