TypeScript项目中immutable数据使用及迁移

2,057 阅读3分钟

Immutable使用及迁移策略

Immutable使用问题

先说明一下在项目中一些immutableJS容易使用不当的场景

  • 避免过度使用toJS()

toJS() 是一个昂贵(性能)的函数,并且与使用 Immutable.JS 的目的相违背,应尽量避免使用。

  • 避免在selector中使用toJS()

carbon.png

  • 避免在 mapStateToProps 中使用 toJS()

使用 toJS() 将 Immutable.JS 对象转换为 JavaScript 对象时,每次都会返回一个新的对象。如果在 mapStateToProps 中执行此操作,则会导致组件在每次 state tree 更改时都认为该对象已更改,因此会触发不必要的重新渲染。

carbon (1).png

  • 避免在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不可变数据结构的需求。

优点

  1. 保留原生操作对象的方式
  2. 强类型关联,和typescript
  3. 和第三方库配合使用简单
  4. 体积小巧(14k)

缺点

  1. 从draft中读取大对象性能较差
  2. 不支持set,map(有workaround github.com/immerjs/imm…

Immer和Immutable在typescript下使用比较(以reducer为例)

JS + Immutable

demo1.png

TS + Immutable
Immutable需要通过class继承Record来定义类型(Record具体使用可参考zhuanlan.zhihu.com/p/41413783),对于多层结构需要嵌套多层Record解决,对于项目中如CardEdit的reducer定义起来会非常繁琐。
以下是嵌套一层的例子
demo2 (4).png


TS + Immer
Immer保留了原始对象的操作,与ts可以无缝结合,相比immutable在reducer的写法上更简洁。
demo3.png

Immutable迁移ts策略

目前immutable在项目中使用较多,主要分布于reducer和ETLModel。
迁移难点:

  1. store一般较为扁平化,我们项目中某些结构嵌套层数很多
  2. 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部分可以复用。


参考