写 React / Vue 项目时为什么要在组件中写 key,其作用是什么?
扩展: diff 算法
前言:虚拟 DOM
我们知道 Vue 的 virtual dom(操作DOM,是一个很好性能的一件事情)。因为数据的更新肯定会引起 DOM 的更新,那我们就尽可能的去复用原来的 DOM。 所谓的 virtual dom,也就是虚拟节点。它通过 JS 的 Object 对象模拟 DOM 中的节点,然后再通过特定的 render 方法将其渲染成真实的 DOM 节点。 dom diff 则是通过 JS 层面的计算,返回一个 patch 对象,即补丁对象,在通过特定的操作解析 patch 对象,完成页面的重新渲染。Vue 的 diff 位于 path.js 中。 网上找的图,可以帮助理解。
DOM DIFF
比较两棵 DOM 树的差异是 Virtual DOM 算法最核心的部分.简单的说就是新旧虚拟dom 的比较,如果有差异就以新的为准,然后再插入的真实的dom中,重新渲染。、 借网络一张图片说明:
比较只会在同层级进行, 不会跨层级比较。
比较后会出现四种情况:
- 此节点是否被移除 -> 添加新的节点
- 属性是否被改变 -> 旧属性改为新属性
- 文本内容被改变-> 旧内容改为新内容
- 节点要被整个替换 -> 结构完全不相同 移除整个替换
比较规则:
同层级进行比较,从上(根节点)到下,从左到右。把不同的打上标记,放到数组里面去,统一交给patch处理。
关于 Key
渲染数据时,带 key,能不能提高性能呢,其实这个问题,得分两种情况来看;
-
官网推荐的使用key,文档也有说明, 只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。.但是真实的情况是,这种模式只适用于渲染简单的无状态组件。对于大多数场景来说,列表组件都有自己的状态。
举个例子:一个新闻列表,可点击列表项来将其标记为"已访问",可通过tab切换“娱乐新闻”或是“社会新闻”。
-
说到底,key 的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。
带上唯一key虽然会增加开销,但是对于用户来说基本感受不到差距,而且能保证组件状态正确,这应该就是为什么推荐使用唯一id作为key的原因。
上图中,B1,B2 是相同的节点,可能只是属性不一样。当 无 key 时,在 diff 比较时,不知道是移动还是更新,会直接把 B1 更新成了 B2,把 B2 更新成了 B1,此时 E,F节点是没办法复用的。
但是,当有 key 时,B1,B2,就有了唯一的标识,就变成了 B1 和 B2 的移动,而E,F也可以复用了。