到目前为止,我看到的大多数“入门样板”和一些关于 react/redux 的帖子都鼓励使用immutable.js来解决可变性问题。我个人依赖Object.assign
或传播运算符来处理这个问题,因此并没有真正看到 immutable.js 的优势,因为它增加了额外的学习,并从用于可变性的 vanilla js 技术转变了一点。我试图找到转换的正当理由,但未能找到,因此我在这里询问为什么它如此受欢迎。
使用 immutable.js 优于 Object.assign 或扩展运算符的优点
这都是关于效率的。
持久数据结构
持久数据结构通过始终产生新的数据结构而在发生变异时保持其自身的先前版本。为了避免昂贵的克隆,只存储与先前数据结构的差异,而它们之间共享交集。这种策略称为结构共享。因此,持久化数据结构比使用Object.assign
或 扩展运算符进行克隆要高效得多。
Javascript 中持久化数据结构的缺点
不幸的是,Javascript 本身不支持持久数据结构。这就是 immutable.js 存在的原因,并且它的对象与普通的旧 Javascript 有很大不同Object
。这会导致更冗长的代码和大量持久数据结构到原生 Javascript 数据结构的转换。
关键问题
immutable.js 的结构共享的好处(效率)何时超过其缺点(冗长、转换)?
我猜这个库只有在拥有大量和广泛对象和集合的大型项目中才有回报,因为克隆整个数据结构和垃圾收集变得更加昂贵。
我已经为多个不可变库创建了性能基准,脚本和结果位于immutable-assign(GitHub 项目)中,这表明immutable.js针对写操作进行了优化,比 Object.assign() 更快,但是,它读取操作较慢。以下是基准测试结果的摘要:
-- Mutable
Total elapsed = 50 ms (read) + 53 ms (write) = 103 ms.
-- Immutable (Object.assign)
Total elapsed = 50 ms (read) + 2149 ms (write) = 2199 ms.
-- Immutable (immutable.js)
Total elapsed = 638 ms (read) + 1052 ms (write) = 1690 ms.
-- Immutable (seamless-immutable)
Total elapsed = 31 ms (read) + 91302 ms (write) = 91333 ms.
-- Immutable (immutable-assign (created by me))
Total elapsed = 50 ms (read) + 2173 ms (write) = 2223 ms.
因此,是否使用 immutable.js 将取决于您的应用程序类型及其读写比率。如果你有很多写操作,那么 immutable.js 将是一个不错的选择。
理想情况下,您应该在引入任何性能优化之前分析您的应用程序,但是,不变性是必须尽早决定的设计决策之一。当您开始使用immutable.js 时,您需要在整个应用程序中使用它以获得性能优势,因为使用 fromJS() 和 toJS() 与普通 JS 对象互操作的成本非常高。
我认为 ImmutableJs 的主要优势在于它的数据结构和速度。当然,它也强制执行不变性,但无论如何你都应该这样做,所以这只是一个额外的好处。
例如,假设您的减速器中有一个非常大的对象,并且您想更改该对象的一小部分。由于不可变性,您不能直接更改对象,但必须创建对象的副本。您可以通过复制所有内容来实现(在 ES6 中使用扩展运算符)。
所以有什么问题?复制非常大的对象非常慢。ImmutableJS 中的数据结构做了一些叫做结构共享的事情,你实际上只改变你想要的数据。您未更改的其他数据在对象之间共享,因此不会被复制。
这样做的结果是高效的数据结构,具有快速的写入。
ImmutableJS 还为深层对象提供了简单的比较。例如
const a = Immutable.Map({ a: Immutable.Map({ a: 'a', b: 'b'}), b: 'b'});
const b = Immutable.Map({ a: Immutable.Map({ a: 'a', b: 'b'}), b: 'b'});
console.log(a.equals(b)) // true
没有这个,你需要某种深度比较函数,这也需要很多时间,而这里的根节点包含整个数据结构的散列(不要引用我,这就是我记得的方式,但比较总是即时的),因此O(1)
无论对象大小如何,比较总是即时的。
这在 ReactshouldComponentUpdate
方法中特别有用,您可以使用此equals
函数比较 props,该函数立即运行。
当然,也有缺点,如果你混合 immutablejs 结构和常规对象,可能很难分辨什么是什么。此外,您的代码库中充斥着 immutableJS 语法,这与常规 Javascript 不同。
另一个缺点是,如果您不打算使用深度嵌套的对象,它会比普通的旧 js 慢一点,因为数据结构确实有一些开销。
只有我的 2 美分。
immutable.js 的优点是它强制执行一个不可变的 redux 存储(你可能忘记扩展,或者在保护你的存储免受修改方面做得很草率)并且与操作符或扩展操作符相比简化了很多reducer Object.assign
(两者都很浅)大大地!)。
话虽如此,我已经在一个大的 Angular 1.x 项目中使用了 redux+immutable,但也有缺点:性能问题,不清楚什么是不可变的,什么不是,Angular 不能在 .js 中使用 immutable.js 结构ng-repeat
,等等。不确定不过,关于 React,很想听听意见。