key 是什么,能做什么,怎么做的?
- key 是给每一个 vnode 的唯一 id 标识,依赖它我们可以 更准确、更快速 的找到 oldvnode 中对应的 vnode 节点。
- 更准确:在 sameNode 函数中,根据 key 可以避免 就地复用
- 就地复用:数组渲染调换位置,没有 key 节点位置不变内容更新,有 key 节点位置互换
- 更快速:map 比遍历更快:利用 key 的唯一性生成 map 映射查找对应节点,比遍历方式查找更快速
- map 映射是基于 hash 值的线性查找时间复杂度为 O(1),而数组遍历时间复杂度为 O(n)
- 更准确:在 sameNode 函数中,根据 key 可以避免 就地复用
作用
- key 的作用主要是为了高效的更新虚拟 DOM。
- Keys 是 React 中用于追踪列表中哪些元素被修改、被添加、被移除等的辅助标志
- 在开发中我们要保证 key 在同级元素中具有唯一性
- 在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新创建的还是被移动而来的元素,从而减少不必要的元素重渲染。
- React 还需要借助 Key 来判断元素与本地状态的关联关系
实例描述
//this.state.users内容
this.state = {
users: [{id:1,name: '张三'}, {id:2, name: '李四'}, {id: 2, name: "王五"}],
....//省略
}
render() {
return(
<div>
<h3>用户列表</h3>
{this.state.users.map(u => <div key={u.id}>{u.id}:{u.name}</div>)}
</div>
)
}
- 效果:
上面代码在 dom 渲染挂载后,用户列表只有 张三 和 李四 两个用户,王五 并没有展示处理,主要是因为 react 根据 key 认为 李四 和 王五 是同一个组件,导致第一个被渲染,后续的会被丢弃掉。 - 这样,有了 key 属性后,就可以与组件建立了一种对应关系,react 根据 key 来决定是销毁重新创建组件还是更新组件。
- key 相同,若组件属性有所变化,则 react 只更新组件对应的属性;没有变化则不更新。
- key 值不同,则 react 先销毁该组件 (有状态组件的 componentWillUnmount 会执行),然后重新创建该组件(有状态组件的 constructor 和 componentWillUnmount 都会执行)
渲染数组中 key 存在的必要性
- 从 babel 转换后 React.createElement 中的代码可以看出,其它元素之所以不是必须需要 key 是因为不管组件的 state 或者 props 如何变化,这些元素始终占据着 React.createElement 固定的位置,这个位置就是天然的 key。而由数组创建的组件可能由于动态的操作导致重新渲染时,子组件的位置发生了变化。
渲染数组 index 作为 key 的实践
- 在 list 数组中,用 key 来标识数组创建子组件时,若数组的内容只是作为纯展示,而不涉及到数组的动态变更,其实是可以使用 index 作为 key 的。
- 但是,若涉及到数组的动态变更,例如数组新增元素、删除元素或者重新排序等,这时 index 作为 key 会导致展示错误的数据。
{this.state.data.map((v,idx)=><Item key={idx} v={v} />)}
// 开始时:['a','b','c']=>
<ul>
<li key="0">a <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">c <input type="text"/></li>
</ul>
// 数组重排 -> ['c','b','a'] =>
<ul>
<li key="0">c <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">a <input type="text"/></li>
</ul>
- 组件重新 render 得到新的虚拟 dom;
- 新老两个虚拟 dom 进行 diff,新老版的都有 key=0 的组件,react 认为同一个组件,则只可能更新组件;
- 然后比较其 children,发现内容的文本内容不同(由 a--->c),而 input 组件并没有变化,这时触发组件的 componentWillReceiveProps 方法,从而更新其子组件文本内容;
- 因为组件的 children 中 input 组件没有变化,其又与父组件传入的 props 没有关联,所以 input 组件不会更新(即其 componentWillReceiveProps 方法不会被执行),导致用户输入的值不会变化。
网友评论