Immutable使用及迁移策略
Immutable使用问题
先说明一下在项目中一些immutableJS容易使用不当的场景
- 避免过度使用toJS()
toJS() 是一个昂贵(性能)的函数,并且与使用 Immutable.JS 的目的相违背,应尽量避免使用。
- 避免在selector中使用toJS()
- 避免在
mapStateToProps
中使用toJS()
使用 toJS() 将 Immutable.JS 对象转换为 JavaScript 对象时,每次都会返回一个新的对象。如果在 mapStateToProps 中执行此操作,则会导致组件在每次 state tree 更改时都认为该对象已更改,因此会触发不必要的重新渲染。
- 避免在Dumb 组件中使用 Immutable.JS
dumb 组件应该是纯粹的,它们应该在给定相同的输入的情况下产生相同的输出,并不具有外部依赖性。如果你将这一一个组件作为 props 传递给一个 Immutable.JS 对象,那么你需要依赖 Immutable.JS 来提取 props 的值,并以其他的方式操纵它。
Immutable本身的问题
- 一旦使用 Immutable.JS 封装数据,你必须使用 Immutable.JS 的
get()
和getIn()
属性访问器来访问它。
这将会在整个代码库中传播 Immutable.JS,包括潜在组件,你可能不喜欢拥有这种外部依赖关系。你的整个代码库必须知道哪些应该是 Immutable.JS 对象,哪些不是。这也会使得当你想从应用程序中移除 Immutable.JS 变得非常困难。且难以和第三方库配合使用。
- 需要fromJS()来构建初始immutable模型,当对象较大性能损耗较大。
- 读取性能较差stackoverflow.com/questions/4…
- 由于toJS()的不当使用会使性能大打折扣
- 包体积较大(61.3k)
- API相对比较复杂
- 和typescript类型兼容性较差(下面会有详细比较)
- 调试不友好
Immer
Immer 是 mobx 的作者写的一个 immutable 库,核心实现是利用 ES6 的 proxy,几乎以最小的成本实现了 js 的不可变数据结构,简单易用、体量小巧、设计巧妙,满足了我们对JS不可变数据结构的需求。
优点
缺点
- 从draft中读取大对象性能较差
- 不支持set,map(有workaround github.com/immerjs/imm…)
Immer和Immutable在typescript下使用比较(以reducer为例)
JS + Immutable
TS + Immutable
Immutable需要通过class继承Record来定义类型(Record具体使用可参考zhuanlan.zhihu.com/p/41413783),对于多层结构需要嵌套多层Record解决,对于项目中如CardEdit的reducer定义起来会非常繁琐。
以下是嵌套一层的例子
TS + Immer
Immer保留了原始对象的操作,与ts可以无缝结合,相比immutable在reducer的写法上更简洁。
Immutable迁移ts策略
目前immutable在项目中使用较多,主要分布于reducer和ETLModel。
迁移难点:
- store一般较为扁平化,我们项目中某些结构嵌套层数很多
- ETLModel结构较复杂,且使用了immutableJS更新数据
保留immutableJS,采用Record定义类型 | 从immutableJS迁至immer | |
---|---|---|
迁移成本 | 中 | 高 |
后期可维护性 | 中 | 高 |
类型兼容性 | 差 | 好 |
说明 | 可先迁移部分结构简单的reducer,等immutable社区有更好的方案再迁移剩余部分。 目前项目中结构较复杂的reducer有: - admin-setting - card-connect - card-drill - card-edit - card-view - data-center - data-source - user-info |
- 可采用逐步迁移的策略,使用redux的combineReducers可兼容使用immutableJS和immer实现的reducer - ETLModel使用immer改写难度不大,immutableJS中的get(In),set(In)可以用lodash get和set替换,path部分可以复用。 |