一、shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
return true;
}
- nextProps: 表示下一个props
- nextState: 表示下一个state的值。
默认返回true,让react执行更新
举例:
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) { //判断color是否改变
return true;
}
if (this.state.count !== nextState.count) { //判断count是否改变
return true;
}
return false; //如果两个都不改变,则不重新渲染
}
只有当 props.color 或者 state.count 的值改变才需要更新时,你可以使用 shouldComponentUpdate 来进行检查
使用 Immutable
我们了解到使用?Immutable 可以给?React ?应用带来性能的优化,主要体现在减少渲染的次数
在做react 性能优化的时候,为了避免重复渲染,我们会在shouldComponentUpdate() 中做对比,当返回true 执行render 方法
Immutable 通过is 方法则可以完成对比,而无需像一样通过深度比较的方式比较
二、PureComponent
在上面例子中,shouldComponentUpdate ?仅检查了?props.color ?或?state.count ?是否改变。如果这些值没有改变,那么这个组件就不会更新。如果组件更复杂一下,你可以使用类“浅比较”的模式来检查props和state的所有的字段,以此来决定是否组件需要更新。react已经提供了一个好的帮手来帮实现这种模式:只要继承?React.PureComponent ?就行了
例子:
class CounterButton extends React.PureComponent { //继承PureComponent默认进行浅比较,决定
//是否重新渲染
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
大部分情况下,你可以使用 React.PureComponent 来代替手写 shouldComponentUpdate。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话(如数组元素、对象属性),浅比较会有遗漏,那你就不能使用它了。当数据结构很复杂时,情况会变得麻烦,这时还是需要自己手动写shouldComponentUpdate( 不采用继承?PureComponent ?的方式), 在内部自己实现比较逻辑,决定是否重新渲染。
例如:
//此时color和count为obj的两个属性,如果采用继承PureComponent的方式,进行浅比较,但一直为同个对象
//这时即使内部属性改变,也检测不出来,导致一直不重新渲染
shouldComponentUpdate(nextProps, nextState) {
if (this.props.obj.color !== nextProps.color) { //判断obj.color是否改变
return true;
}
if (this.state.obj.count !== nextState.count) { //判断obj.count是否改变
return true;
}
return false; //如果两个属性都不改变,则不重新渲染
}
三、避免使用内联函数
如果我们使用内联函数,则每次调用render 函数时都会创建一个新的函数实例,如下:
import React from "react";
export default class InlineFunctionComponent extends React.Component {
render() {
return (
<div>
<h1>Welcome Guest</h1>
<input type="button" onClick={(e) => { this.setState({inputValue: e.target.value}) }} value="Click For Inline Function" />
</div>
)
}
}
我们应该在组件内部创建一个函数,并将事件绑定到该函数本身。这样每次调用?render ?时就不会创建单独的函数实例,如下:
import React from "react";
export default class InlineFunctionComponent extends React.Component {
setNewStateData = (event) => {
this.setState({
inputValue: e.target.value
})
}
render() {
return (
<div>
<h1>Welcome Guest</h1>
<input type="button" onClick={this.setNewStateData} value="Click For Inline Function" />
</div>
)
}
}
四、使用 React Fragments 避免额外标记
用户创建新组件时,每个组件应具有单个父标签。父级不能有两个标签,所以顶部要有一个公共标签,所以我们经常在组件顶部添加额外标签div
这个额外标签除了充当父标签之外,并没有其他作用,这时候则可以使用fragement
其不会向组件引入任何额外标记,但它可以作为父级标签的作用,如下所示:
export default class NestedRoutingComponent extends React.Component {
render() {
return (
<>
<h1>This is the Header Component</h1>
<h2>Welcome To Demo Page</h2>
</>
)
}
}
五、事件绑定方式
从性能方面考虑,在render 方法中使用bind 和render 方法中使用箭头函数这两种形式在每次组件render 的时候都会生成新的方法实例,性能欠缺
而constructor 中bind 事件与定义阶段使用箭头函数绑定这两种形式只会生成一个方法实例,性能方面会有所改善
常见的绑定方式有如下:
-
render方法中使用bind -
render方法中使用箭头函数 -
constructor中bind -
定义阶段使用箭头函数绑定
1、render方法中使用bind
如果使用一个类组件,在其中给某个组件/元素一个onClick 属性,它现在并会自定绑定其this 到当前组件,解决这个问题的方法是在事件函数后使用.bind(this) 将this 绑定到当前组件中
class?App?extends?React.Component?{
??handleClick()?{
????console.log('this?>?',?this);
??}
??render()?{
????return?(
??????<div?onClick={this.handleClick.bind(this)}>test</div>
????)
??}
}
这种方式在组件每次render 渲染的时候,都会重新进行bind 的操作,影响性能
2、render方法中使用箭头函数
通过ES6 的上下文来将this 的指向绑定给当前组件,同样在每一次render 的时候都会生成新的方法,影响性能
class?App?extends?React.Component?{
??handleClick()?{
????console.log('this?>?',?this);
??}
??render()?{
????return?(
??????<div?onClick={e?=>?this.handleClick(e)}>test</div>
????)
??}
}
3、constructor中bind
在constructor 中预先bind 当前组件,可以避免在render 操作中重复绑定
class?App?extends?React.Component?{
??constructor(props)?{
????super(props);
????this.handleClick?=?this.handleClick.bind(this);
??}
??handleClick()?{
????console.log('this?>?',?this);
??}
??render()?{
????return?(
??????<div?onClick={this.handleClick}>test</div>
????)
??}
}
4、定义阶段使用箭头函数绑定
跟上述方式三一样,能够避免在render 操作中重复绑定,实现也非常的简单,如下:
class?App?extends?React.Component?{
??constructor(props)?{
????super(props);
??}
??handleClick?=?()?=>?{
????console.log('this?>?',?this);
??}
??render()?{
????return?(
??????<div?onClick={this.handleClick}>test</div>
????)
??}
}
综合上述,方式四是最优的事件绑定方式
五、懒加载组件
从工程方面考虑,webpack 存在代码拆分能力,可以为应用创建多个包,并在运行时动态加载,减少初始包的大小
而在react 中使用到了Suspense 和?lazy 组件实现代码拆分功能,基本使用如下:
const?johanComponent?=?React.lazy(()?=>?import(/*?webpackChunkName:?"johanComponent"?*/?'./myAwesome.component'));
?
export?const?johanAsyncComponent?=?props?=>?(
??<React.Suspense?fallback={<Spinner?/>}>
????<johanComponent?{...props}?/>
??</React.Suspense>
);
六、useMemo性能优化
import React, { useState, memo, useMemo } from 'react'
// 子组件
// function Child({ userInfo }) {
// console.log('Child render...', userInfo)
// return <div>
// <p>This is Child {userInfo.name} {userInfo.age}</p>
// </div>
// }
// 类似 class PureComponent ,对 props 进行浅层比较
const Child = memo(({ userInfo }) => {
console.log('Child render...', userInfo)
return <div>
<p>This is Child {userInfo.name} {userInfo.age}</p>
</div>
})
// 父组件
function App() {
console.log('Parent render...')
const [count, setCount] = useState(0)
const [name, setName] = useState('小宗')
// const userInfo = { name, age: 20 }
// 用 useMemo 缓存数据,有依赖
const userInfo = useMemo(() => {
return { name, age: 21 }
}, [name])
return <div>
<p>
count is {count}
<button onClick={() => setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo}></Child>
</div>
}
export default App
总结:
- React默认会更新所以子组件
- class组件使用SCU和PureComponent做优化
- Hooks中使用useMemo,但优化原理是相同的
七、useCallback做性能优化
import React, { useState, memo, useMemo, useCallback } from 'react'
// 子组件,memo 相当于 PureComponent
const Child = memo(({ userInfo, onChange }) => {
console.log('Child render...', userInfo)
return <div>
<p>This is Child {userInfo.name} {userInfo.age}</p>
<input onChange={onChange}></input>
</div>
})
// 父组件
function App() {
console.log('Parent render...')
const [count, setCount] = useState(0)
const [name, setName] = useState('双越老师')
// 用 useMemo 缓存数据
const userInfo = useMemo(() => {
return { name, age: 21 }
}, [name])
// function onChange(e) {
// console.log(e.target.value)
// }
// 用 useCallback 缓存函数
const onChange = useCallback(e => {
console.log(e.target.value)
}, [])
return <div>
<p>
count is {count}
<button onClick={() => setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo} onChange={onChange}></Child>
</div>
}
export default App
总结:
- useMemo 缓存数据
- useCallback 缓存函数
- 两者是Hooks的常见优化策略
|