日期:2024年11月22日

渲染列表

现在,有这样一组数据,需要在页面中呈现:

const students = ['孙悟空', '猪八戒', '沙和尚'];

如果想直接显示,可以直接将数组插入到JSX中,JSX会自动对数组展开并显示。但这样一来数据知识直接显示,不会添加任何的结构。如果希望将数组中的元素放到一个无序列表中显示,就需要对列表做一些处理了。

<ul>
   <li>孙悟空</li>
   <li>猪八戒</li>
   <li>沙和尚</li>
</ul>

可以使用for循环对其进行处理:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const items = [];
​
for(let i=0; i<students.length; i++){
    items.push(<li>{stus[i]}</li>);
}
​
const ele = <ul>{items}</ul>

上边操作的本质其实就是根据一个旧数组students生成了一个新数组items:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const items = [
    <li>孙悟空</li>,
    <li>猪八戒</li>,
    <li>沙和尚</li>,
];

这个操作我们也可以通过map()方法来完成:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const items = students.map(item => <li>{item}</li>);
const ele = <ul>{items}</ul>

map()本身就是一个函数,所以我们完全可以直接将函数调用写在JSX中间:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const ele = <ul>{students.map(item => <li>{item}</li>)}</ul>

很显然使用map()比使用for循环要优雅很多,所以这也是我们在React中的常用方式。

警告

输出一个列表时,无论我们使用的for循环还是map()方法,当你打开控制台时你一定会看到一行红色的内容,它大概内容是:Warning: Each child in a list should have a unique “key” prop.

Warning表示一个警告,警告表示它并不是一个特别严重的错误,但你最好把它处理了,所以当你在React项目中看到这个玩意的时候一定一定要想办法去除掉它。

为什么会报出这个警告呢?事情要往前回溯一下,我们已经知道React是通过虚拟DOM来操作元素的,而React为了提升操作的性能,在DOM发生变化时只会去修改那些发生了变化的元素,这样就大大的减少了DOM操作从而提升了页面渲染的速度。

React怎么知道哪些元素发生变化呢?其实也不难,React每次渲染都会生成一个由React元素构成的树(当然这棵树也对应着一课DOM元素构成的树,但是这里不太重要),React每次重新渲染都会生成一个新的React元素树,在页面刷新前,React会通过内部的diff算法对两个树中的React元素进行比较,并且找到那些发生变化的元素,并将他们的变化在真实的DOM元素上体现出来。

这个算法听上去很神奇,其实很简单就是按照顺序比较两个元素,第一个和第一个比,第二个和第二个比,没有变化就放过,有变化就记录。

扯了这么一长串,和上边的警告有什么关系吗?上边的例子中,我们把一个数组直接在JSX中呈现了出来。当我们第一次渲染时,列表可能是这样的:

<ul>
    <li>孙悟空</li>
    <li>猪八戒</li>
    <li>沙和尚</li>
</ul>

假使数组发生了变化(这里还没讲怎么变,想象一下吧!),页面需要重新渲染,变成了这样:

<ul>
    <li>孙悟空</li>
    <li>猪八戒</li>
    <li>沙和尚</li>
    <li>唐僧</li>
</ul>

这里我们在列表的最后增加了一个唐僧,那么前边的元素顺序是不变的,孙悟空第一个,猪八戒第二,沙和尚第三。所以这里在应用修改时只会修改最后一个li,对前边的三个不会有任何影响!性能非常棒!

但是,加入唐僧不想在最后,它想去第一个位置,像这样:

<ul>
    <li>唐僧</li>
    <li>孙悟空</li>
    <li>猪八戒</li>
    <li>沙和尚</li>
</ul>

这时问题就来了,唐僧第一,孙悟空第二…换句话说,所有元素的顺序都变了,那么React在比较元素不同时,它并不能知道孙悟空从第一变到了第二个,它会任务是唐僧替换了孙悟空,孙悟空替换了猪八戒,猪八戒替换了沙和尚,然后又增加了一个沙和尚,导致页面渲染是四个li都被重新渲染了!如果列表里有100个元素,那么就是100个都得重新渲染,性能不好!

那么这种情况,我们就需要让React知道谁是孙悟空,谁又是唐僧。在React内部就设定了一个key属性,key属性可以作为React元素的唯一标识,和html中id类似。在创建一个列表时,我们可以为列表的每一个元素指定一个唯一的key,React就可以根据key而不是位置来比较元素,这样一来无论元素的位置如何改变,都不会导致过多的元素渲染,因为有了key以后,React就不再通过位置来比较两个元素了。

上边的例子可以这样修改:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const ele = <ul>{students.map(item => <li key={item}>{item}</li>)}</ul>

添加了key属性以后警告就消失了,设置key还有一些要求:

  1. key必须在当前列表的元素中是唯一的
  2. 一个元素的key最好是固定的

key不需要全局唯一,只需在当前列表中唯一即可。元素的key最好是固定的,这里直接举个反例,有些场景我们会使用元素的索引为key像这种:

const students = ['孙悟空', '猪八戒', '沙和尚'];
const ele = <ul>{students.map((item, index) => <li key={index}>{item}</li>)}</ul>

上例中,我使用了元素的索引(index)作为key来使用,但这有什么用吗?没用!因为index是根据元素位置的改变而改变的,当我们在前边插入一个新元素时,所有元素的顺序都会一起改变,那么它和React中按顺序比较有什么区别吗?没有区别!而且还麻烦了,唯一的作用就是去除了警告。所以我们开发的时候偶尔也会使用索引作为key,但前提是元素的顺序不会发生变化,除此之外不要用索引做key。

5 1 投票
文章评分
订阅评论
提醒
guest

2 评论
最旧
最新 最多投票
内联反馈
查看所有评论
Ark
Ark
2 年 前

很清晰, 谢谢老师

Dnzzk2
Dnzzk2
2 年 前

老师我出现两个孙悟空,不是冲突了

2
0
希望看到您的想法,请您发表评论x