- 保证渲染的性能
- 第一个例子
- 坏实践
- 好实践
- 第二个例子
- 坏实践
- 又一个坏实践
- 好实践
- 坏实践
- 好实践
- 第一个例子
- 参考资料:
保证渲染的性能
为了保障组件的性能, 我们有的时候会从组件渲染的角度出发.
更干净的render函数? 这个概念可能会有点让人疑惑.
其实在这里干净是指我们在shouldComponentUpdate这个生命周期函数里面去做浅比较, 从而避免不必要的渲染.
关于上面的干净渲染, 现有的一些实现包括React.PureComponent, PureRenderMixin, recompose/pure 等等.
第一个例子
坏实践
class Table extends PureComponent {render() {return (<div>{this.props.items.map(i =><Cell data={i} options={this.props.options || []}/>)}</div>);}}
这种写法的问题在于{this.props.options || []}- 这种写法会导致所有的Cell都被重新渲染即使只有一个cell发生了改变. 为什么会发生这种事呢?
仔细观察你会发现, options这个数组被传到了Cell这个组件上, 一般情况下, 这不会导致什么问题. 因为如果有其他的Cell组件, 组件会在有props发生改变的时候浅对比props并且跳过渲染(因为对于其他Cell组件, props并没有发生改变). 但是在这个例子里面, 当options为null时, 一个默认的空数组就会被当成Props传到组件里面去. 事实上每次传入的[]都相当于创建了新的Array实例. 在JavaScript里面, 不同的实例是有不同的实体的, 所以浅比较在这种情况下总是会返回false, 然后组件就会被重新渲染. 因为两个实体不是同一个实体. 这就完全破坏了React对于我们组件渲染的优化.
好实践
const defaultval = []; // <--- 也可以使用defaultPropsclass Table extends PureComponent {render() {return (<div>{this.props.items.map(i =><Cell data={i} options={this.props.options || defaultval}/>)}</div>);}}
第二个例子
在render函数里面调用函数也可能导致和上面相同的问题.
坏实践
class App extends PureComponent {render() {return <MyInputonChange={e => this.props.update(e.target.value)}/>;}}
又一个坏实践
class App extends PureComponent {update(e) {this.props.update(e.target.value);}render() {return <MyInput onChange={this.update.bind(this)}/>;}}
在上面的两个坏实践中, 每次我们都会去创建一个新的函数实体. 和第一个例子类似, 新的函数实体会让我们的浅比较返回false, 导致组件被重新渲染.
所以我们需要在更早的时候去bind我们的函数.
好实践
class App extends PureComponent {constructor(props) {super(props);this.update = this.update.bind(this);}update(e) {this.props.update(e.target.value);}render() {return <MyInput onChange={this.update}/>;}}
坏实践
class Component extends React.Component {state = {clicked: false};onClick() {this.setState({clicked: true})}render() {// 如果options为空的话, 每次都会创建一个新的{test:1}对象const options = this.props.options || {test: 1};return <Somethingoptions={options}// New function created each renderonClick={this.onClick.bind(this)}// New function & closure created each renderonTouchTap={(event) => this.onClick(event)/>}}
好实践
class Component extends React.Component {state = {clicked: false};options = {test: 1};onClick = () => {this.setState({clicked: true})};render() {// Options这个对象只被创建了一次.const options = this.props.options || this.options;return <Somethingoptions={options}onClick={this.onClick} // 函数只创建一次, 只绑定一次onTouchTap={this.onClick} // 函数只创建一次, 只绑定一次/>}}
参考资料:
- @esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f"">https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f
- https://github.com/nfour/js-structures/blob/master/guides/react-anti-patterns.md#pure-render-immutability
- Optimizing React Rendering
