React性能优化思路
软件的性能优化思路就像生活中去看病,大致是这样的:
使用工具来分析性能瓶颈(找病根)
尝试使用优化技巧解决这些问题(服药)
使用工具测试性能是否确实有提升(疗效确认)
初识react只是为了尽快完成项目,后期进行代码审查时候发现有很多地方需要优化,因此做了个小结。
- Code Splitting
- shouldComponentUpdate避免重复渲染
- 使用不可突变数据结构
- 组件尽可能的进行拆分、解耦
- 列表类组件优化
- bind函数优化
- 不要滥用props
- ReactDOMServer进行服务端渲染组件
Code Splitting
Code Splitting 可以帮你“懒加载”代码,如果你没办法直接减少应用的体积,那么不妨尝试把应用从单个 bundle 拆分成单个 bundle + 多份动态代码的形式。
webpack提供三种代码分离方法,详情见webpack官网
在此,主要了解一下第三种动态导入的方法。
1、例如可以把下面的import方式
改写成动态 import 的形式,让首次加载时不去加载 math 模块,从而减少首次加载资源的体积。
2、例如引用react的高阶组件react-loadable进行动态import。
loader: () => import('./my-component'),loading: Loading,});
export default class App extends React.Component {
render() {
return
}
}
上面的代码在首次加载时,会先展示一个 loading-component,然后动态加载 my-component 的代码,组件代码加载完毕之后,便会替换掉 loading-component
shouldComponentUpdate避免重复渲染
当一个组件的props或者state改变时,React通过比较新返回的元素和之前渲染的元素来决定是否有必要更新实际的DOM。当他们不相等时,React会更新DOM。
在一些情况下,你的组件可以通过重写这个生命周期函数shouldComponentUpdate来提升速度, 它是在重新渲染过程开始前触发的。 这个函数默认返回true,可使React执行更新。
为了进一步说明问题,引用官网的图解释一下,如下图( SCU表示shouldComponentUpdate,绿色表示返回true(需要更新),红色表示返回false(不需要更新);vDOMEq表示虚拟DOM比对,绿色表示一致(不需要更新),红色表示发生改变(需要更新)):
根据渲染流程,首先会判断shouldComponentUpdate(SCU)是否需要更新。如果需要更新,则调用组件的render生成新的虚拟DOM,然后再与旧的虚拟DOM对比(vDOMEq),如果对比一致就不更新,如果对比不同,则根据最小粒度改变去更新DOM;如果SCU不需要更新,则直接保持不变,同时其子元素也保持不变。
- C1根节点,绿色SCU、红色vDOMEq,表示需要更新。
- C2节点,红色SCU,表示不需要更新,同时
- C4、C5作为其子节点也不需要检查更新。
- C3节点,绿色SCU、红色vDOMEq,表示需要更新。
- C6节点,绿色SCU、红色vDOMEq,表示需要更新。
- C7节点,红色SCU,表示不需要更新。
- C8节点,绿色SCU,表示React需要渲染这个组件;绿色vDOMEq,表示虚拟DOM一致,不更新DOM。
因此,我们可以通过根据自己的业务特性,重载shouldComponentUpdate,只在确认真实DOM需要改变时,再返回true。一般的做法是比较组件的props和state是否真的发生变化,如果发生变化则返回true,否则返回false。引用官网的案例。
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
在以上代码中,shouldComponentUpdate只检查props.color和state.count的变化。如果这些值没有变化,组件就不会更新。当你的组件变得更加复杂时,你可以使用类似的模式来做一个“浅比较”,用来比较属性和值以判定是否需要更新组件。这种模式十分常见,因此React提供了一个辅助对象来实现这个逻辑 - 继承自React.PureComponent。
大部分情况下,你可以使用React.PureComponent而不必写你自己的shouldComponentUpdate,它只做一个浅比较。但是当你比较的目标为引用类型数据,浅比较会忽略属性或状态突变的情况,此时你不能使用它,此时你需要关注下面的不可突变数据。
附:数据突变(mutated)是指变量的引用没有改变(指针地址未改变),但是引用指向的数据发生了变化(指针指向的数据发生变更)。例如const x = {foo:'foo'}。x.foo='none' 就是一个突变。
使用不可突变数据结构
引用官网中的例子解释一下突变数据产生的问题。例如,假设你想要一个ListOfWords组件来渲染一个逗号分隔的单词列表,并使用一个带了点击按钮名字叫WordAdder的父组件来给子列表添加一个单词。以下代码并不正确:
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 这段内容将会导致代码不会按照你预期的结果运行
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (