当前位置: 首页 > 科技观察

为什么React中的列表渲染需要加Key

时间:2023-03-13 23:28:04 科技观察

大家好,我是前端西瓜哥,今天来学习下React中的列表渲染需要加key的原因。在React中,我们经常需要渲染列表,比如显示好友列表。常见的写法是使用Array.prototype.map方法将数组形式的数据映射到JSX.Element数组,嵌入到组件返回的JSX.Element中,如下:FriendList(){const[items,setItems]=useState(['前端西瓜哥','小明','张三']);return(

    {items.map((item)=>({item}))}
);}您需要为每个项目提供一个键属性作为区分不同项目的标识符。如果你不添加键,React会警告你:Warning:E??achchildinalistshouldhaveaunique"key"prop。为什么需要钥匙?在回答这个问题之前,我们先简单了解一下React的DOMDiff算法的原理。当状态发生变化时,React会按需批量更新真实的DOM树,生成新的UI。底层做的工作是:用diff比较新旧虚拟DOM树,计算patch,打到真实DOM树上。为了效率,React的diff算法有限制:只能比较同级节点,不能跨级比较。如果元素的类型不一样(比如从p变成div),它们就不一样了,整个旧的子树就会被销毁,调用它下面的组件的卸载钩子,然后一个全新的树会被创建,这会消耗大量的性能。如果类型相同,则进行打补丁操作(如更新className和label下的文本内容)。但是这样做会有问题。如果同级多节点的位置发生了变化,但是因为同一个索引位置不匹配,发现不能复用,就必须销毁一棵树,重新创建一棵树。效率太低了。所以React为开发者提供了标记节点的键来优化Reactdiff算法,告诉React一个节点没有被移除或者不能在原地重用,而只是改变了它的位置,让React更新位置。如果列表渲染器不提供密钥会怎样?如果不提供密钥,React无法确定节点是否已移动。React只会比较相同位置的两个节点,如果它们是同一类型(例如,都是li元素),它会比较props的差异并修补props。因为列表渲染通常是同一个类型,当位置发生变化时,很可能会触发节点就地复用的效果,但是不用担心树的破坏重建。当没有提供密钥时,就地多路复用有时可以正确呈现。除了一种情况,节点有自己的内部状态,最经典的就是输入框。functionFriendList(){const[items,setItems]=useState(['前端西瓜哥','小明','张三']);constswap=()=>{[items[0],items[1]]=[items[1],items[0]];设置项目([...项目]);};返回(
    {items.map((item)=>(
  • {item}
  • ))}
{swap();}}>swap
);}我们给第一个,在第二个输入框中输入内容。然后点击“交换”按钮交换数组第一个和第二个元素的位置。然后我们看到输入前面的文字被正确交换了,但是输入框里的内容却没有。原因是React做了就地多路复用,但是输入不传props,所以不需要打props补丁,还是一样的。如何解决这个问题呢?添加密钥。为了让React知道你的节点需要移动,你必须写:items.map((item)=>({item}))anotherwithoutkey一个缺点是无法充分利用React.memo的组件缓存能力,因为就地多路复用会改变传入的props。当列表渲染的key使用数组索引时会发生什么?效果和不使用key一样,依然是新旧节点相同的索引位置对比,只是控制台不会打印警告。应该使用什么值作为键?对于节点,需要给key分配一个唯一的id,一般是一个数组的id,比如后端返回的好友列表的好友id。const[items,setItems]=useState([{id:5,name:'前端西瓜哥'},{id:9,name:'小明'},{id:87,name:'张三'},{id:91,name:'前端西瓜'}]);constlist=items.map((item)=>({item.name}));如果后端没有返回id,你可以用id生成器手动添加一个id,虽然它并不优雅。例如:constitems=['前端西瓜哥','张三'];constgenId=(()=>{让i=0;return()=>{returni++;}})();constitemsWithId=items.map(item=>({id:genId(),val:item}));//[{id:0,val:'前端西瓜'},{id:1,val:'张三'}]对了,这个key只需要在同级节点间唯一即可,不需要各级key都唯一。另外,如果你想确保你的列表在渲染后直到它被销毁之前不会改变位置,你可以使用数组索引作为键。最后,对于列表的渲染,我们需要提供区分节点的键。React的DOMDiff算法会根据key调整节点的位置,保证部分节点的渲染状态与内部状态相关。一般来说,键值应该是唯一的,通常来自后端返回的数据。当你确认列表的位置不会改变时,你可以使用数组索引作为键来去除烦人的警告提示。需要注意的是key并不是列表渲染独有的,普通节点也可以使用key。