如果想提高性能就需要知道什么地方会影响性能?
从过往的经验和实践中,影响网页性能最大的因素就是浏览器的重绘和回流,React背后的虚拟DOM就是尽可能的减少浏览器的重绘和回流。
那在此之上,我们还能做什么来防止不必要的渲染?
在这之前,先跟我一起了解下***函数式编程***
什么是函数式编程?
函数式编程起源于数学,假设我们定义一个加法的方法f,然后改变输入为f(1,3),那么无论这个方法的上下文,无论什么时间调用多少次,返回结果都是4,用数学表达就是f(x,y) = z,给定输入x,y作用在f上,始终结果是z
函数式编程讲究的三个原则:
1.相同的输入得到相同的输出
2.没有副作用
3.不依赖外部的状态(方法内的状态都只在方法的生命周期内存活,不能在方法中使用共享变量,因为这样会给方法带来不可知对的因素)
那说了这么多函数式编程有什么好处呢?
1、函数式编程不依赖外部状态这一特点,使得我们可以利用CPU在分布式集群上做并行计算,这对于多种科学计算和资源密集型计算任务是非常核心的一点,让计算机高效处理这类任务变动的可能 2、相同的输入得到相同对的输出,这让我们的代码变得可预测,可以非常方便的进行方法级别的测试。
说完函数式编程,我们再回头说说react的性能优化 《深入React技术栈中》这样描述的“ react的设计是有函数式编程的基因的,react组件本身就是纯函数,react的createElement方法保证了组件是纯净的,即传入相同的props得到一定的虚拟dom,整个过程可预测”。
那么优化的时候我们可以考虑通过拆分组件为子组件,进而对组件进行更细粒度的控制。
那接下来就要考虑如何做更细粒度控制,避免多次无用渲染?
react官方有提供PureComponent类,凡是继承自这个类的组件,react都会默认在shouldComponentUpdate中替你做这样一件事,浅比较你的新传入的props和state是否和现在的props,state。所谓的浅比较就是只比较props和state中数据的引用地址,引用地址不改变就不会重新渲染。
很显然,问题来了,如果我的数据结构嵌套很深,那岂不是会忽略掉深层次的数据变化,而导致页面不渲染? 因此react官方提醒道,继承PureComponent之前最好props,state的结构简单,对于嵌套深的结构就不要继承PureComponent了
那对于深层次的数据结构就这样放弃使用puerRender了吗?答案当然不是了,不过先不急着说这个,我们先说对于结果简单的可以继承PureComponent的我们要注意些什么?
<MeetingList style = {{'width':'200px','height':'200px','overFlow':'hidden'}}>
<Item items = {this.props.items.filter(item=>item.val>10)}>
复制代码
这种直接将props设置成对象或者数组的方式,每次都会触发重新渲染,哪怕值没有改变。
原因在于每次调用react组件都会重新创建组件,就算传入的数组或对象值没有改变,但是引用地址发生改变了。
解决方法,将对象提前赋值为常量,不直接使用字面量即可 2、
<input onChange = this.change.bind(this)>
复制代码
设置props方法并通过bind绑定this的方式,每次都会触发重新渲染
原因在于bind会返回一个函数,每次执行bind返回的函数的引用地址改变了
解决办法采用箭头函数
3、
class NameItem extends Component {
render(){
return (
<Item >
<span>a child</span>
</Item>
)
}
}
复制代码
对于设置了子组件的react组件,每次都会重新渲染。
原因在于,组件编译之后其实是这样的
<Item children = {React.createElement('span',{},'a child')}>
复制代码
children引用改变了,导致每次都会重新渲染
解决办法让NameItem(也就是Item的父组件)继承pureComponent,根据浅比较策略不会对Item的children进行深比较,也就不会重新渲染
说完简单的数据结构,我们再来说下对于props和state比较复杂的数据结构应该怎么处理,答案是采用immutable。
简单说下immutable
持久化数据存储:对immutable对象进行修改的时候都会返回一个新的immutable对象,也就是使用旧数据创建新数据时,包证了旧数据可用 结构化共享:如果树中一个节点变化,则只修改这个节点和受它影响的父节点,其他节点实现共享,避免了深拷贝带来的性能问题
如何使用immutable实现pureRender?
答案是在shouldComponentUpdate中利用immutable进行深比较,抛弃之前的PureComponent的浅比较
import React from 'react';
import {is} from 'immutable';
class App extends Component {
shouldComponentUpdate(nextProps,nextState){
const thisProps = this.props || {};
const thisState = this.state || {};
if(Object.keys(thisProps).length !== Object.keys(nextProps.keys).length ||
Object.keys(thisProps).length !== Object.keys(nextProps.keys).length){
return true;
}
for(const key in nextProps){
if(nextProps.hasOwnProperty(key) && !is(thisProps[key],nextProps[key])){
return true;
}
}
for(const key in nextState){
if(nextState.hasOwnProperty(key) && !is(thisState[key],nextState[key])){
return true;
}
}
return false;
}
}
复制代码