各位同学大家晚上好,今天来说说react相关的东西。《从零玩转React全家桶核心(21)》正在更新,视频版请登录官网(www.it666.com)查看,或者扫码直达:


Diff算法

开发中我们知道,数据的更新:例如DOM节点、组件属性的增加、删除、修改等引起界面的变化。在React中,每次组件的状态或属性更新,组件的render方法都会返回一个新的虚拟DOM对象,用来展示新的UI结构。

UI = render(data)

虚拟DOM(Virtual DOM)

与虚拟DOM相对应的是真实DOM,真实DOM即DOM对象,其是对结构化文本的抽象表达,它定义了访问HTML文档对象的一套属性、方法和事件。

页面中的每一个HTML元素对应一个DOM节点,例如常见的p元素、div元素,HTML元素的层级嵌套关系也会体现在DOM节点的层级上,所有DOM节点构成了DOM树。如下图:

DOM树 图片来自百度

在传统的前端开发中(如下图),直接对DOM进行增删改查的操作时,每一次修改都会引起浏览器对网页的重排重绘

而这个过程是很耗时且消耗性能的。因此在开发中性能优化方面应该「尽量减少DOM操作」


虚拟DOM则是对真实DOM的抽象,其表现为普通的Javascript对象,当需要操作DOM时,可以操作虚拟DOM,访问Javascript对象远比访问真实DOM快,因此避免了真实DOM效率低下的情况。

dom-diff

当界面上某一节点发生变化时,React会通过比较两次虚拟DOM结构的变化找出差异部分,更新到真实DOM上,从而减少最终要在真实DOM上执行的操作,提高程序执行效率。这一过程称为React的调和过程(Reconciliation),其中的关键是比较两个树形结构的Diff算法。

值得注意的是:「在Diff算法中,比较的两方是新的虚拟DOM和旧的虚拟DOM,而不是虚拟DOM和真实DOM」,只是最终Diff的结果会更新到真实DOM上。

两个假设

React比较两棵树的差异基于以下两个假设:

  1. Two elements of different types will produce different trees.(两个不同类型的元素会生成不同类型的树)

  2. The developer can hint at which child elements may be stable across different renders with a key prop.(同一层次的一组节点,可以通过唯一的key属性来进行区分在render过程中是否发生了变化。)

React对DOM树进行了分层,只会对同一层的节点进行数据比较(如下图)。另外,React认为在Web UI中DOM节点的跨层级移动操作比较少,甚至可以忽略不计。


比较两棵树的差异是从树的根节点开始,根节点的类型不同,则React执行的操作也不同。

根节点是相同DOM元素类型

如果两个根节点是相同类型的DOM元素,React会保留根节点对应的DOM元素,对树形结构根节点上的属性和内容比对,然后只更新虚拟DOM树和真实DOM树中对应节点的属性和内容即可。

根节点是相同组件类型

如果两个根节点是相同类型的组件,节点发生变化时,对应的组件实例不会被销毁,只是会执行更新操作,同步变化的属性到虚拟DOM树上,此时组件实例的componentWillReceiveProps()componentWillUpdate()会被调用。

开发人员可以通过shouldComponentUpdate()进行优化,如果发现根本没有必要重新渲染,那就可以直接返回false,减少不必要的更新。

根节点类型不同

当根节点类型发生变化时包括两种:DOM元素的变化和Reac组件的变化。

当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树。

当根节点为不同类型的组件时,React会认为这两个DOM树结构不同,将之前的组件直接删除,然后创建新组件。

组件类型发生变化时,在旧的虚拟DOM树被拆除的过程中,旧的DOM元素类型的节点会被销毁,旧的React组件实例的componentWillUnmount()会被调用;

在重建的过程中,新的DOM元素会被插入DOM树中,新的组件实例的componentWillMount()componentDidMount()方法会被调用。重建后的新的虚拟DOM树又会被映射更新到真实DOM树中。

性能优化

避免不必要的组件渲染

当组件的propsstate发生变化时,组件的render方法会被重新调用,返回一个新的虚拟DOM对象。

但在一些情况下,组件是没有必要重新调用render方法的。例如:如果只修改了父组件的数据, 并没有修改子组件的数据, 并且子组件中也没有用到父组件中的数据,那么子组件还是会重新渲染, 子组件的render方法还是会重新执行。

shouldComponentUpdate()这个方法的默认返回值是true,如果返回false,组件此次的更新将会停止,也就是后续的componentWillUpdate()render()等方法都不会再被执行。我们可以根据组件自身的业务逻辑决定返回true还是false,从而避免组件不必要的渲染。

当然也建议使用PureComponent类来代替shouldComponentUpdate()

shouldComponentUpdate(nextProps, nextState, nextContext) {  // return true;  // return false;  if(this.state.age !== nextState.age){    return true;  }  return false;}

另外,要尽可能少在render()减少变量的新建和绑定函数,例如实现单击事件的时候,如果在render()函数里面绑定this,则每次渲染的时候都会重新执行一遍函数。所以提前绑定函数也是一种比较好的开发习惯,可以起到部分优化作用。

使用key

React会根据key索引元素,在render前后,判定拥有相同key值的元素是否为同一个元素。


给列表元素添加key,告诉React除了和同层同位置比, 还需要和同层其它位置对比。注意添加key属性时,需要保持其“唯一性”且“稳定性”。

const numbers = [1, 2, 3, 4, 5];const listItems = numbers.map((number) =>  <li key={number.toString()}>    {number}li>);

不推荐使用数组下标作为key,看起来key值是唯一的,但是却不是稳定不变的,随着数组值的不同,同样一个实例在不同的更新过程中在数组中的下标完全可能不同,把下标当做key就让React完全混乱了。

使用性能检测工具

1.React Developer Tools for Chrome

主要用来检测页面使用的React代码是否是生产环境版本。当访问网页时,如果插件图标的背景色是黑色的,就表示当前网页使用的是生产环境版本的React;


如果插件图标的背景色是红色的,就表示当前网页使用的是开发环境版本的React


在项目中上线时尽量使用「生产环境版本的库」也能优化一部分性能,以npm runstart启动时,使用的React是开发环境版本的React库,包含大量警告消息,以帮助我们在开发过程中避免一些常见的错误。开发环境版本的库不仅体积更大,而且执行速度也更慢,不适合在生产环境中使用。

对于create-react-app脚手架创建的项目,执行npm run build,就会构建生产环境版本的React库。

2.Chrome Performance Tab

在开发模式下,通过Chrome浏览器提供的Performance工具观察组件的挂载、更新、卸载过程及各阶段使用的时间。

3.React Perf/Jest/Enzyme等测试库

尽可能多地使用小组件

例如使用MobX和React结合时,@observer包装的组件会追踪render方法中使用的所有可观测对象,组件越小,组件追踪的对象越少,引起组件重新渲染的可能性也越小。且在层级越低的小组件中解引用对象属性时,由这个属性的变化导致的重新渲染的组件的数量也越少。

慎用{...this.props}

只传递组件需要的props,传得太多或者层次传得太深时,都会加重shouldComponentUpdate里的数据的比较负担,因此慎用spread attributes(形如<Component{...props}/>)。

小结:性能优化本身涉及多方面因素,建议不要将性能优化的精力浪费在对整体性能提高不大的代码上,且不要过早优化。在实际开发中,也有其他途径值得实践,例如:使用CDN处理静态资源、启用Gzip压缩、DNS预解析等等

视频版教学请登录官网查看~

react循环key值_React性能优化的几个知识点相关推荐

  1. react循环key值_React源码揭秘(三):Diff算法详解

    编者按:本文作者奇舞团前端开发工程师苏畅. 代码参照React 16.13.1 什么是Diff 在前两篇文章中我们分别介绍了 React 的首屏渲染流程1和组件更新流程2,其中 首屏渲染会渲染一整棵 ...

  2. react循环key值_01 React快速入门(一)——使用循环时对于‘key’报错处理

    问题描述: 在刚开始接触react学习的时候,编写一个小功能时,使用了map来循环一个数组中的数据,输出到前端页面,代码调试运行后正常显示,但是打开控制台却发现有一条关于"key" ...

  3. sybase 设置默认值_SYBASE性能优化

    带你轻松接触Sybase ASE15.0.2性能优化 调整共享内存: /sbin/sysctl -w kernel.shmmax=3416386150 shmmax是最大共享内存段,假如服务器上没有别 ...

  4. react循环的值为什么要有key_react中为何推荐设置key

    我想很多人刚开始使用react框架的时候,和我刚开始一样迷惘.只知道 map返回的元素项推荐设置key,却不知为何要设置key.当初我也在网上找了一些相关的文章,却看得一塌糊涂,始终没明白为啥要设置k ...

  5. MySQL优化filler值_MySQL 性能优化神器 Explain 使用分析

    简介 MySQL 提供了一个 EXPLAIN 命令, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息, 以供开发人员针对性优化. EXPLAIN 命令用法十分简单, 在 ...

  6. react实现异步插件_react项目优化之webpack

    开门见山,由于我们项目的前端只有一个 bundle ,所有代码都在一个js文件里,随着功能不断的堆叠,体积已经到无法忍受的地步了(gzip后即将突破300k),导致首屏的时间不停的涨啊涨,最近一周富裕 ...

  7. react循环的值为什么要有key_糊盒粘箱为什么要检查表面覆膜电晕值

    糊盒粘箱为何必须检测表面覆膜的电晕值?不测后果很严重!文末有福利! 电晕值 膜表面张力(经达因笔测试出来的值为电晕值,下简称电晕值)是表面处理决定性的标准,一般表面张力/电晕值越高越容易上色或被黏附. ...

  8. react 组件遍历】_从 Context 源码实现谈 React 性能优化

    (给前端大全加星标,提升前端技能) 转自:魔术师卡颂 学完这篇文章,你会收获: 了解Context的实现原理 源码层面掌握React组件的render时机,从而写出高性能的React组件 源码层面了解 ...

  9. React Native性能优化总结

    React Native开源已经接近2年时间,京东.携程.58同城等互联网公司都在使用,公司于今年也开始使用,并推广到各个新项目.本文重点分享我们遇到的一些问题以及优化方案. 一.为什么会引入Reac ...

最新文章

  1. 好书推荐:《零基础快速入行入职软件测试工程师》学测试一本就
  2. suse linux c 编译环境,SUSE 11中安装GCC开发环境
  3. 三年python面试题_300道Python面试题
  4. Qt::ConnectionType(信号与槽的传递方式)
  5. 批处理之判断文件是否存在
  6. JumpServer 开源堡垒机 快速部署
  7. 3.07 检测两个表中是否有相同的数据
  8. android.mk 编译32位_C/C++初学者常见编译错误及其解决办法
  9. ORACLE成果,天天10问(四)
  10. java基于springboot高校后勤报修管理系统
  11. 洛谷试炼场---提高历练地 普及练习场 新手村 入门难度
  12. MaxMall开源微信分销系统
  13. After 500:写500篇博客其实和写一篇是一样的
  14. v8 8.0以上版本中,V8_COMPRESS_POINTERS引发的崩溃
  15. SolidWorks六角螺母添加倒角2种方法
  16. Spring MVC框架在进行表单提交,自动封装成对象提交,在以对象的形式入参
  17. 公务员报考二级专业目录计算机类,浙江省考报名,所学专业所属的二级专业目录如何找?...
  18. SVN和Git的 区别
  19. 感染了Arp欺骗病毒(木马)
  20. 区块链软件开发的搭建环境

热门文章

  1. linux中文快捷键,Linux系统快捷键最全合集
  2. Qt线程间信号槽传递自定义数据类型(qRegisterMetaType的使用)
  3. python调整屏幕缩放比例_python实现按长宽比缩放图片 python的turtle怎样缩放界面吧...
  4. MongoDB复制集搭建主服务器模拟切换
  5. 在没有DOM操作的日子里,我是怎么熬过来的(终结篇)
  6. python基础6-函数的参数
  7. 《中国人工智能学会通讯》——12.44 分类型数据的定义
  8. FBI或被允许隐瞒解锁iPhone技术 不向苹果公开
  9. poj_2299Ultra-QuickSort,树状数组离散化
  10. linux文件末尾 m,Linux7-删除nginx配置文件末尾的^M字符