摘要:对同一层级的子节点进行处理时,会根据进行简要的复用。二性能优化方案由于中性能主要耗费在于阶段的算法,因此性能优化也主要针对算法。此时最常用的优化方案即为方法。或者直接使用,原理一致。
一、从React原理谈起
react是什么?
react是用于构建用户界面的JS框架。因此react只负责解决view层的渲染。
react做了什么?
Virtual Dom模型
生命周期管理
setState机制
diff算法
React patch、事件系统
react的Virtual Dom模型
virtual dom 实际上是对实际Dom的一个抽象,是一个js对象。react所有的表层操作实际上是在操作virtual dom。
经过diff算法会计算出virtual dom的差异,然后将这些差异进行实际的dom操作更新页面。
react的生命周期 setState机制 理想情况:setState是“异步”的,调用setState只会提交一次state修改到队列中,不会直接修改this.state。
等到满足一定条件时,react会合并队列中的所有修改,触发一次update流程,更新this.state。
因此setState机制减少了update流程的触发次数,从而提高了性能。
由于setState会触发update过程,因此在update过程中必经的生命周期中调用setState会存在循环调用的风险。
另外用于监听state更新完成,可以使用setState方法的第二个参数,回调函数。在这个回调中读取this.state就是已经批量更新后的结果。
特殊情况:在实际开发中,setState的表现有时会不同于理想情况。主要是以下两种。
在mount流程中调用setState。
在setTimeout/Promise回调中调用setState。
在第一种情况下,不会进入update流程,队列在mount时合并修改并render。
在第二种情况下,setState将不会进行队列的批更新,而是直接触发一次update流程。
这是由于setState的两种更新机制导致的,只有在批量更新模式中,才会是“异步”的。
diff算法diff算法用于计算出两个virtual dom的差异,是react中开销最大的地方。
传统diff算法通过循环递归对比差异,算法复杂度为O(n3)。
react diff算法制定了三条策略,将算法复杂度从 O(n3)降低到O(n)。
WebUI中DOM节点跨节点的操作特别少,可以忽略不计。
拥有相同类的组件会拥有相似的DOM结构。拥有不同类的组件会生成不同的DOM结构。
同一层级的子节点,可以根据唯一的ID来区分。
针对这三个策略,react diff实施的具体策略是
diff对树进行分层比较,只对比两棵树同级别的节点。跨层级移动节点,将会导致节点删除,重新插入,无法复用。
diff对组件进行类比较,类相同的递归diff子节点,不同的直接销毁重建。
diff对同一层级的子节点进行处理时,会根据key进行简要的复用。两棵树中存在相同key的节点时,只会移动节点。
另外,在对比同一层级的子节点时。diff算法会以新树的第一个子节点作为起点遍历新树,寻找旧树中与之相同的节点。
如果节点存在,则移动位置。如果不存在,则新建一个节点。
在这过程中,维护了一个字段lastIndex,这个字段表示已遍历的所有新树子节点在旧树中最大的index。
在移动操作时,只有旧index小于lastIndex的才会移动。
这个顺序优化方案实际上是基于一个假设,大部分的列表操作应该是保证列表基本有序的。
可以推倒倒序的情况下,子节点列表diff的算法复杂度为O(n2)。
由于react中性能主要耗费在于update阶段的diff算法,因此性能优化也主要针对diff算法。
1.减少diff算法触发次数
减少diff算法触发次数实际上就是减少update流程的次数。
正常进入update流程有三种方式:
setState
setState机制在正常运行时,由于批更新策略,已经降低了update过程的触发次数。
因此,setState优化主要在于非批更新阶段中(timeout/Promise回调),减少setState的触发次数。
常见的业务场景即处理接口回调时,无论数据处理多么复杂,保证最后只调用一次setState。
父组件render
父组件的render必然会触发子组件进入update阶段(无论props是否更新)。此时最常用的优化方案即为shouldComponentUpdate方法。
最常见的方式为进行this.props和this.state的浅比较来判断组件是否需要更新。或者直接使用PureComponent,原理一致。
需要注意的是,父组件的render函数如果写的不规范,将会导致上述的策略失效。
// Bad case // 每次父组件触发render 将导致传入的handleClick参数都是一个全新的匿名函数引用。 // 如果this.list 一直都是undefined,每次传入的默认值[]都是一个全新的Array。 // hitSlop的属性值每次render都会生成一个新对象 class Father extends Component { onClick() {} render() { returnthis.onClick()} list={this.list || []} hitSlop={{ top: 10, left: 10}}/> } } // Good case // 在构造函数中绑定函数,给变量赋值 // render中用到的常量提取成模块变量或静态成员 const hitSlop = {top: 10, left: 10}; class Father extends Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.list = []; } onClick() {} render() { return } }
forceUpdate
其中forceUpdate方法调用后将会直接进入componentWillUpdate阶段,无法拦截,因此在实际项目中应该弃用。
其他优化策略
使用shouldComponentUpdate钩子,根据具体的业务状态,减少不必要的props变化导致的渲染。如一个不用于渲染的props导致的update。
合理设计state,不需要渲染的state,尽量使用实例成员变量。
不需要渲染的props,合理使用context机制,或公共模块(比如一个单例服务)变量来替换。
2.正确使用diff算法
不使用跨层级移动节点的操作。
对于条件渲染多个节点时,尽量采用隐藏等方式切换节点,而不是替换节点。
尽量避免将后面的子节点移动到前面的操作,当节点数量较多时,会产生一定的性能问题。
END.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95872.html
摘要:对同一层级的子节点进行处理时,会根据进行简要的复用。或者直接使用,原理一致。 一、从React原理谈起 react是什么? showImg(https://segmentfault.com/img/bVbcYvf?w=1140&h=384); react是用于构建用户界面的JS框架。因此react只负责解决view层的渲染。 react做了什么? Virtual Dom模型 生命周期...
摘要:欢迎来我的个人站点性能优化其他优化浏览器关键渲染路径开启性能优化之旅高性能滚动及页面渲染优化理论写法对压缩率的影响唯快不破应用的个优化步骤进阶鹅厂大神用直出实现网页瞬开缓存网页性能管理详解写给后端程序员的缓存原理介绍年底补课缓存机制优化动 欢迎来我的个人站点 性能优化 其他 优化浏览器关键渲染路径 - 开启性能优化之旅 高性能滚动 scroll 及页面渲染优化 理论 | HTML写法...
摘要:工程实践立足实践,提示实际水平内联函数与性能很多关于性能优化的文章都会谈及内联函数,其也是常见的被诟病为拖慢性能表现的元凶之一不过本文却是打破砂锅问到底,论证了内联函数并不一定就会拖慢性能,过度的性能优化反而会有损于应用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...
阅读 766·2023-04-25 15:13
阅读 1388·2021-11-22 12:03
阅读 815·2021-11-19 09:40
阅读 1896·2021-11-17 09:38
阅读 1701·2021-11-08 13:18
阅读 648·2021-09-02 15:15
阅读 1759·2019-08-30 15:54
阅读 2623·2019-08-30 11:12