现在,有这样一组数据,需要在页面中呈现:
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还有一些要求:
- key必须在当前列表的元素中是唯一的
- 一个元素的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。
很清晰, 谢谢老师
老师我出现两个孙悟空,不是冲突了