React 性能优化之showComponentUpdate与memo

662 阅读5分钟

参考链接

React.PureComponent

React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.

React.PureComponent 是与React.Component相似的。不同的地方是React.Component 没有自己实现了shouldComponentUpdate()方法,但是React.PureComponent实现了简单的比对prop与state数据;

注意RenderItem继承PureComponent/Component时点击不同按钮时的打印输出 
class RenderItem extends PureComponent{
	render(){
		console.log('render',this.props.value);
		return <div>{this.props.value}</div>
	}
}
class User extends Component{
	constructor(props){
		super(props);
		this.state = {
			list: [1,2,3,4]
		}
	}
	componentDidMount(){
		console.log(this.props);
	}
	render(){
		return (
			<>
				<div onClick={()=>this.setState({list: [...this.state.list,this.state.list.length + 1]})}>刷新数组,添加元素</div>
				{this.state.list.map(item=><RenderItem key={item} value={item} />)}
			</>
		)
	}
}

React.memo

React.memo is a higher order component.

If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.

React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change.

By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.

React.memo是一个高阶组件;

如果你的组件使用同样的props会渲染出一样的效果的话,你可以使用一个叫做React.memo的组件来包裹(你的组件)通过缓存结果的方式来促进性能优化;这个代表React会路过渲染组件,并重用最后一个渲染的结果;

React.memo只会检查 prop的变化,如果被包裹的方法组件实现了useStte,useReducer或者useContext钩子,当state/context变化时它仍然会渲染;

通常情况下它只会简单地比较props对象中的复杂数据,如果你想完全控制比较方法,你可以提供一个自定义方法作为第二个参数 ;

//与前面是一样的。。。
import React ,{memo, Component, PureComponent,} from 'react';
class RenderItem2 extends Component{
	render(){
		console.log('render',this.props.value);
		return <div>{this.props.value}</div>
	}
}
const RenderItem = memo(RenderItem2);

自己实现shouldComponentUpdate

Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.

shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used.

This method only exists as a performance optimization. Do not rely on it to “prevent” a rendering, as this can lead to bugs. Consider using the built-in PureComponent instead of writing shouldComponentUpdate() by hand. PureComponent performs a shallow comparison of props and state, and reduces the chance that you’ll skip a necessary update.

If you are confident you want to write it by hand, you may compare this.props with nextProps and this.state with nextState and return false to tell React the update can be skipped. Note that returning false does not prevent child components from re-rendering when their state changes.

We do not recommend doing deep equality checks or using JSON.stringify() in shouldComponentUpdate(). It is very inefficient and will harm performance.

Currently, if shouldComponentUpdate() returns false, then UNSAFE_componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.

使用shouldComponentUpdate() 可以使React 知道组件的输出是否匹配当前的prop/state的修改影响的。默认情况下每次state修改都会重新渲染,大部分情况下你应该根据默认行为。

shouldComponentUpdate() 会在每次rendering渲染前被调用,当新props/新state已经收到时。 默认返回true, 这个不会在初次渲染/强制渲染时调用;

这个方法只会因为性能优化而存在,不要依赖它来做』阻止渲染『操作,因为它可能会引发BUG的。 我们可以考虑使用React.PureComponent 来现代手写一个shouldComponentUpdate(). PureComponent 是一个简单比对prop/state来达到降低你必要更新的次数;

如果你考虑手写shouldComponentUpdate(), 你要比对this.props 与 nextProps, this.state 与 nextState,并返回false 告诉React 这次更新可以路过。 记住在子组件stte变化时,即使返回false不会阻止子组件的重新渲染;

我们不好建议使用深度比对/使用JSON.stringify()进行比对。它们是非常低效且降低性能;

shouldComponentUpdate()返回false ,UNSAFE_componentWillUpdate(),render(),componentDidUpdate()不会被触发。 未来shouldComponentUpdate()会被当作是提示而还是严格的指令,而且返回false 也会重新渲染组件;

	shouldComponentUpdate(nextProps,nextState):boolean{
		return nextProps.value !== this.props.value;
	}

useMemo hook

Returns a memoized value.

Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.

Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.

If no array is provided, a new value will be computed on every render.

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

返回一个缓存了的值

传入一个"create" 创建方法和一组依赖。 使用useMode只会在其中一个依赖值变更时,才会重新计算缓存值。缓存帮助我们在每次渲染时避开很多昂贵的计算。

记住传入到useMemo的方法会在渲染时执行。不要做任何你平时不会在渲染方法做的事情。 例如side effect 需要在useEffect,不是在useMemo

如果没有依赖数组传入,每次渲染时,都会重新计算;

你可以使用useMemo做性能优化,但是它不保证永远不变;未来,React会选择忘记上一个计算结果,并在下次渲染时重新计算。例如,释放屏幕外的组件的缓存。写代码时注意在没有useMemo时也可以运行,并把它用作性能优化;

const RenderItem = function(props){
	const plus100 = (val)=>{
		console.log('call plus 100',val);
		return val + 100;
	}
	let valuePlus100 = useMemo(()=>plus100(props.value),[props.value]);
	
	return <div>{valuePlus100}</div>
}