| 3 | C | 2 | 3 | oldIndex(2) index: 新集合的遍历下标。 oldIndex:当前节点在老集合中的下标。 maxIndex:在新集合访问过的节点中,其在老集合的最大下标值。 操作一栏中只比较oldIndex和maxIndex: 当oldIndex>maxIndex时,将oldIndex的值赋值给maxIndex 当oldIndex=maxIndex时,不操作 当oldIndex 上面的例子仅仅是在新旧集合中的节点都是相同的节点的情况下,那如果新集合中有新加入的节点且旧集合存在 需要删除的节点,那么 diff 又是如何对比运作的呢? | index | 节点 | oldIndex | maxIndex | 操作 | | — | — | — | — | — | | 0 | B | 1 | 0 | oldIndex(1)>maxIndex(0),maxIndex=oldIndex | | 1 | E | - | 1 | oldIndex不存在,添加节点E至index(1)的位置 | | 2 | C | 2 | 1 | 不操作 | | 3 | A | 0 | 3 | oldIndex(0) 注:最后还需要对旧集合进行循环遍历,找出新集合中没有的节点,此时发现存在这样的节点D,因此删除节点D,到此 diff 操作全部完成。 同样操作一栏中只比较oldIndex和maxIndex,但是oldIndex可能有不存在的情况: oldIndex存在 当oldIndex>maxIndex时,将oldIndex的值赋值给maxIndex 当oldIndex=maxIndex时,不操作 当oldIndex oldIndex不存在 新增当前节点至index的位置 当然这种diff并非完美无缺的,我们来看这么一种情况: 实际我们只需对D执行移动操作,然而由于D在旧集合中的位置是最大的,导致其他节点的oldIndex < maxIndex,造成D没有执行移动操作,而是A、B、C全部移动到D节点后面的现象。针对这种情况,官方建议: 在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作。当节点数量过大或更新操作过于频繁时,这在一定程度上会影响React的渲染性能。 由于key的存在,react可以准确地判断出该节点在新集合中是否存在,这极大地提高了diff效率。我们在开发过中进行列表渲染的时候,若没有加key,react会抛出警告要求开发者加上key,就是为了提高diff效率。但是加了key一定要比没加key的性能更高吗?我们再来看一个例子: 现在有一集合[1,2,3,4,5],渲染成如下的样子: 1 2 3 4 5 现在我们将这个集合的顺序打乱变成[1,3,2,5,4]。 1.加key 1 1 2 3 3 ========> 2 4 5 5 4 操作:节点2移动至下标为2的位置,节点4移动至下标为4的位置。 2.不加key 1 1 2 3 3 ========> 2 4 5 5 4 操作:修改第1个到第5个节点的innerText 如果我们对这个集合进行增删的操作改成[1,3,2,5,6]。 1.加key 1 1 2 3 3 ========> 2 4 5 5 6 操作:节点2移动至下标为2的位置,新增节点6至下标为4的位置,删除节点4。 2.不加key 1 1 2 3 3 ========> 2 4 5 5 6 操作:修改第1个到第5个节点的innerText 通过上面这两个例子我们发现: 由于dom节点的移动操作开销是比较昂贵的,没有key的情况下要比有key的性能更好。 通过上面的例子我们发现,虽然加了key提高了diff效率,但是未必一定提升了页面的性能。因此我们要注意这么一点: 对于简单列表页渲染来说,不加key要比加了key的性能更好 根据上面的情况,最后我们总结一下key的作用: 准确判断出当前节点是否在旧集合中 极大地减少遍历次数 应用实践 ============================================================== 示例代码地址:https://github.com/ruichengpi… 页面指定区域刷新 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AgBbpToV-1590739472382)(https://segmentfault.com/img/bVbrxNK?w=840&h=426)] 现在有这么一个需求,当用户身份变化时,当前页面重新加载数据。猛一看过去觉得非常简单,没啥难度的,只要在componentDidUpdate这个生命周期里去判断用户身份是否发生改变,如果发生改变就重新请求数据,于是就有了以下这一段代码: import React from ‘react’; import {connect} from ‘react-redux’; let oldAuthType = ‘’;//用来存储旧的用户身份 @connect( state=>state.user ) class Page1 extends React.PureComponent{ state={ loading:true } loadMainData(){ //这里采用了定时器去模拟数据请求 this.setState({ loading:true }); const timer = setTimeout(()=>{ this.setState({ loading:false }); clearTimeout(timer); },2000); } componentDidUpdate(){ const {authType} = this.props; //判断当前用户身份是否发生了改变 if(authType!==oldAuthType){ //存储新的用户身份 oldAuthType=authType; //重新加载数据 this.loadMainData(); } } componentDidMount(){ oldAuthType=this.props.authType; this.loadMainData(); } render(){ const {loading} = this.state; return ( {`页面1${loading?'加载中...':'加载完成'}`} ) } } export default Page1; 看上去我们仅仅通过加上一段代码就完成了这一需求,但是当我们页面是几十个的时候,那这种方法就显得捉襟见肘了。哪有没有一个很好的方法来实现这个需求呢?其实很简单,利用react diff的特性就可以实现它。对于这个需求,实际上就是希望当前组件可以销毁在重新生成,那怎么才能让其销毁并重新生成呢?通过上面的总结我发现两种情况,可以实现组件的销毁并重新生成。 当组件类型发生改变 当key值发生变化 接下来我们就结合这两个特点,用两种方法去实现。 第一种:引入一个loading组件。切换身份时设置loading为true,此时loading组件显示;切换身份完成,loading变为false,其子节点children显示。 {loading?:children} 第二种:在刷新区域加上一个key值就可以了,用户身份一改变,key值就发生改变。 {children} 第一种和第二种取舍上,个人建议的是这样子的: 如果需要请求服务器的,用第一种,因为请求服务器会有一定等待时间,加入loading组件可以让用户有感知,体验更好。如果是不需要请求服务器的情况下,选用第二种,因为第二种更简单实用。 更加方便地监听props改变 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zt4JNYab-1590739472383)(https://segmentfault.com/img/remote/1460000018914265)] 针对这个需求,我们喜欢将搜索条件封装成一个组件,查询列表封装成一个组件。其中查询列表会接收一个查询参数的属性,如下所示: import React from ‘react’; import {Card} from ‘antd’; import Filter from ‘./components/filter’; import Teacher from ‘./components/teacher’; export default class Demo2 extends React.PureComponent{ state={ filters:{ name:undefined, height:undefined, age:undefined } } handleFilterChange=(filters)=>{ this.setState({ filters }); } render(){ const {filters} = this.state; return {/* 过滤器 */} {/* 查询列表 */} } } 现在我们面临一个问题,如何在组件Teacher中监听filters的变化,由于filters是一个引用类型,想监听其变化变得有些复杂,好在lodash提供了比较两个对象的工具方法,使其简单了。但是如果后期给Teacher加了额外的props,此时你要监听多个props的变化时,你的代码将变得比较难以维护。针对这个问题,我们依旧可以通过key值去实现,当每次搜索时,重新生成一个key,那么Teacher组件就会重新加载了。代码如下: import React from ‘react’; import {Card} from ‘antd’; import Filter from ‘./components/filter’; import Teacher from ‘./components/teacher’; export default class Demo2 extends React.PureComponent{ state={ filters:{ name:undefined, height:undefined, age:undefined }, tableKey:this.createTableKey() } createTableKey(){ return Math.random().toString(36).substring(7); } handleFilterChange=(filters)=>{ this.setState({ filters, //重新生成tableKey tableKey:this.createTableKey() }); } render(){ const {filters,tableKey} = this.state; return {/* 过滤器 */} {/* 查询列表 */} } } 即使后期给Teacher加入新的props,也没有问题,只需拼接一下key即可: react-router中Link问题 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4dvjYkaY-1590739472385)(https://segmentfault.com/img/remote/1460000018914266)] 先看一下demo代码: import React from ‘react’; import {Card,Spin,Divider,Row,Col} from ‘antd’; import {Link} from ‘react-router-dom’; const bookList = [{ bookId:‘1’, bookName:‘三国演义’, author:‘罗贯中’ },{ bookId:‘2’, bookName:‘水浒传’, author:‘施耐庵’ }] export default class Demo3 extends React.PureComponent{ state={ bookList:[], bookId:‘’, loading:true } loadBookList(bookId){ this.setState({ loading:true }); const timer = setTimeout(()=>{ this.setState({ loading:false, bookId, bookList }); clearTimeout(timer); },2000); } componentDidMount(){ const {match} = this.props; const {params} = match; const {bookId} = params; this.loadBookList(bookId); } render(){ 自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。 深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化! 由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新! 如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端) 最后的最后 面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。 需要完整面试题的朋友可以点击蓝色字体免费获取 712199781794)] [外链图片转存中…(img-UftF1rYA-1712199781794)] 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化! [外链图片转存中…(img-Sm9e9n3X-1712199781795)] 由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新! 如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端) 最后的最后 面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。 需要完整面试题的朋友可以点击蓝色字体免费获取 [外链图片转存中…(img-hHagd3Q0-1712199781795)] [外链图片转存中…(img-vfTxhdei-1712199781795)] [外链图片转存中…(img-Lnd9EMSh-1712199781795)] [外链图片转存中…(img-MzJ6nQaR-1712199781796)] 推荐阅读
上一篇
发表评论