React 提供了 PureComponent 之后,我们知道 PureComponent 对性能有一定的提升,但是不是所有组件都应该用 PureComponent?
与 Component 的区别
PureComponent
与 Component
的唯一差别就在于:PureComponent
帮你做了 shouldComponentUpdate
的判断。
shouldComponentUpdate(nextProps, nextState){ return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState); }
shouldComponentUpdate 决定了组件是否调用 render 函数重新进行渲染。
上面这段代码的含义为:当新的 props (即 nextProps)或新的 state (即 nextState)与当前的值相比没有发生变化时,组件不用更新。这里我们用 shallowEqual,只比较 object 的第一层 key value,因为进行 deepEqual 的对比是非常耗性能的。
正确传递 props
由 shallowEqual 引申出来的是 js 中对象的值比对问题。
const newObject = this.state.obj newObject.id = 2; this.setState({ obj: newObject, });
试想如果我们这样更新 state 之后,继承自 PureComponent 的组件会不会更新?
--- 🤤 ---
答案是不会。原因在于:newObject 与 this.state.obj 始终指向同一个内存地址,所以当我们用 shallowEqual 对比 this.state 与 nextState 时,得到的结果是二者相等。也就意味着 PureComponent 不会执行 render。
所以我们一般会这样写:
const newObject = { ...this.state.obj, id: 2, }; this.setState({ obj: newObject, }); this.state.obj === nextState.obj; // false
于是发展到后来,我们有了 Immutable 的概念:「当一个数据被创建之后,就永远不会变了」。如果需要更新这个数据,你必须重新创建一个。
const obj = { id: 1, text: 'hello', }; obj.text = 'world' // 这样不行,obj 不可被改变 // 你必须要像这样创造一个新的数据 const newObj = { ...obj, text: 'world', };
这样始终保证新传入的 props 或 state 都是新的对象, shallowEqual 就不会再出错了。
--- 😪 ---
一个对象指向同一个引用地址会导致 PureComponent 不更新,相反的,每次都传入一个新对象就会导致 PureComponent 每次都执行 render,与 Component 也就不再有区别。
有下面几种情况会导致 props 每次都传入一个新对象。
- bind a function
<CommentItem likeComment={() => this.likeComment(user.id)} />
- 在 render 中生成新对象往子组件中传递
render() { const { posts } = this.props; const topTen = [...posts].sort((a, b) => b.likes - a.likes).slice(0, 9); return ( <Posts items={topTen} /> ); }
- someArray || [], someObject || {}, ...
render() { const { posts } = this.props; // 当 posts == false 时,传入 [] 相当于每次都传入一个新对象 return ( <Posts items={posts || []} /> ); }
小心踩坑
所以最后,灵魂拷问:是不是应该总是优先使用 PureComponent 而不是 Component ?
这里有两个坑需要注意一下。
- 子组件也需要是纯组件
官方文档中有一句话:
Make sure all the children components are also “pure”.
父组件如果是 PureComponent,当 state 和 props 保持不变时是不会重新渲染的,子组件也就不会重新渲染了。如果你遇到 props 更新时组件无法更新,就可以排查一下是不是组件树的哪一级误用了 PureComponent。
- PureComponent 是不是任何情况下都比 Component 性能好?
看一个比较常见的例子:
render() { const { item } = this.props; return <Post item={item} style={{ 'width': 120 }} />; }
由于每次传入的 style 都是一个新对象,所以我们已经可以预知这个组件是每次都需要 rerender 的。此时 PureComponent 每次都要执行一次 shallowEqual 对比,反而比 Component 更消耗性能。
所以结论是,如果你已经预期到某个组件的 props 或是 state 会「频繁变动」,那就根本不用使用 PureComponent,因为这样反而会变慢。
相关文章