(阅读本文约需 2 分钟)javascript
众所周知,在使用 Redux 时最麻烦的一个部分就是 reducer 的编写,因为 Redux 要求状态是 immutable 的,也就是说,发生变化的状态树必定是一个新的引用。 因此 reducer 常常会写成这样:java
function todoApp(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return Object.assign({}, state, { visibilityFilter: action.filter }) case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { text: action.text, completed: false } ] }) default: return state } } 复制代码
不少人会称之为深克隆,其实并非,这个过程既不是深克隆也不是浅克隆。git
首先咱们来谈谈深克隆是否可行,若是你的 reducer 在每次状态发生变化时都进行深克隆处理,你的 app 毋庸置疑是能够 work 的,Time Travelling 固然也能够用,那么问题会出在哪里呢?github
咱们不妨经过图示来看一下:redux
整个状态树被重建了,这就意味着 PureComponent
和 shouldComponentUpdate
没有实现好的组件都会从新 render。markdown
因此在实际项目中,咱们引入了 Immutable.js,就是为了不写出繁琐或者不正确的 reducer。相似的还有 immer 这样的库。app
Immutable.js 内部会使用 Shared Structure 来避免深克隆,一方面提高了 Immutable.js 自身的性能,另外一方面能帮助 React 更高效地渲染。就像这样:函数
当一个对象中的一个键发生变化时,这个对象中其余键的值不会有任何变化,而引用该对象的对象会产生一份新的引用,以此类推。这样,咱们的状态树就能够像值类型同样进行对比了:oop
节点 4 发生变化,节点 一、2 变化先后必定不相等,可是节点 三、五、6 没有变化仍然是相等的。咱们甚至不用 deepEquals
,对比引用就能够了,由于 Immutable.js 能够保证它们不发生变化。性能
所以,咱们的 React 组件若是采用了 PureComponent,就能自动得到最好的优化,与变化无关的组件也不会从新渲染。
然而在实际使用中,咱们又遇到了问题,即使使用了 Immutable.js,每次更新时仍是有不少无关组件发生更新了。搜查了一遍代码,我发现咱们如今有不少这样的写法:
const mapStateToProps = state => { const user = selectCurrentUser(state) const me = user.toJS() const myTeam = selectMyTeam(state) const team = myTeam && myTeam.toJS() //... return { user, me, myTeam, team /*, ...*/ } } 复制代码
问题就出在 toJS
的调用上,根据文档:
Deeply converts this Keyed collection to equivalent native JavaScript Object.
toJS
会将本来 structure shared 的对象彻底深克隆一遍,全部 PureComponent 又会从新渲染。能够看一下咱们如今的状况:
能够看到,改变了一个与左侧边栏无关的按钮状态的时候,左侧边栏依旧从新渲染了。
下面是去掉了 toJS
调用后的状况:
是否是好多了。
至此咱们也可以得出结论了,React 的渲染性能很大一部分取决于更新的粒度,当咱们的 render 函数已经足够庞大时,咱们可以作的只有分步更新(Fiber 和 Time Slicing 主要解决的问题)和精准更新了。
而要作到精准更新,就必定要处理好状态的变化,其实最简单的方法就是状态扁平化,对象层级越小,咱们的代码里可能出现的问题就越少。另外,尽量将 connect
放置在须要状态的组件外,目前咱们仍是有不少组件过早 connect
,而后将状态一层一层经过 props
传下去,这也是状态对象层级太深(有多深我就不截图了...)致使的。Redux 状态更新(dispatch)时,全部的 Connect(...)
组件都会根据本身的 mapped state 进行更新,越早 connect
的组件越有可能发生更新,而其子组件若是没有处理好 shouldComponentUpdate
就会出现许多无用的更新,白白损失性能。
References: