Redux 整合笔记 七 性能

135 阅读3分钟

性能评估工具

性能时间线

当在开发环境时,只需在URL末尾加上?react_perf。然后查看Google Chrome开发者工具里的Performance标签页

Redux优化

连接正确的组件

前面描述过组件的简单策略:默认情况下从展示型组件开始,一旦它们变得过于臃肿,不访问Redux store就无法维护,就用connect将它们包装起来。

现在,我们将提供工具指导性的原则:如果能避免开销很大的重绘,就连接组件。

自上而下的方法

redux带来的一个好处时,数据所有组件都是潜在可用的,这对数据流动提供了很大的灵活性。

简单策略,虽然简单实用。但有性能成本。由于只有一个入口点,因此只要redux中的数据发生改变,整个APP就会试图重新渲染。因为它们都是App组件的子组件,所以每次App重绘时(在某个action派发后),Header 和 Taskspage也都会尝试重新渲染。

将其他组件连接到redux

若能正确使用connect,就不必编写自己的shouldComponentUpdate逻辑。将Header和TasksPage直接连接到redux,会怎样?每个组件都将使用选择器来获取它们自己所需的数据 - 不多也不少。

这是一种非常强大的理念:可使connect来提高性能而不是用自定义的shouldComponentupdates,而且这也非常适合组织及解耦规模不断增长的APP。

合理吗?这些改动是由性能推动的,但从职责分离的角度看,这样也更有意义。当APP随着时间推移而规模增长时,以即将进行的方式连接其他组件,可防止任何模块变得臃肿与混乱。

mapStateToProps和记忆型选择器

完成上述代码变更后,Header仍会重绘。答案在于更基础的部分 - JS中的引用检查和connect的浅层对比。 src/reducers/index.js

export const getProjects = state => {
	return Object.keys(state.projects.items).map(id => {
    	return projects.items[id];
    })
} 

当考虑connect如何检查任意属性是否已经改变时,问题来了。我们使用connect存储mapStateToProps的返回值,并且当渲染操作被触发时与新的属性进行比较。这是一种浅层相等性比较,而不会深入任何内嵌对象。

这与Header有何关系?为什么组件仍进行不必要的重绘?redux中的每个状态在发生更改后都会运行getProjects,但返回值没有记忆。每次调用getProjects时,无论数据是否已更改,都将返回一个新的对象。因此connect的浅层相等性检查永远不会通过,而react-redux会认为Header有新的数据需要渲染。

解决方案是使用reselect将getProjects更改为记忆现选择器

export const gfetProjects = createSelector(
	[state => state.projects],
    projects => {
    	return  Object.keys(state.projects.items).map(id => {
    	return projects.items[id];
    })
    }
)

最有效的性能优化是“做更少的东西”,这就是此处达到的效果。

connect高级用法的法则

  • 通过connect可避免使用shouldComponentUpdate.
  • 以更精细的方式使用connect,这对规范化数据最有效
  • 以这样的方式使用connect是一种架构选择,而在某些情况下可能产生 一些问题。