Redux + ImmutableJS - 如何垃圾收集太大的存储?

Posted

技术标签:

【中文标题】Redux + ImmutableJS - 如何垃圾收集太大的存储?【英文标题】:Redux + ImmutableJS - how to garbage collect too large store? 【发布时间】:2017-07-01 13:51:03 【问题描述】:

我将 Redux 与 ImmutableJS 一起使用。在我的 SPA(相当复杂的管理系统)中,用户经常将大量数据加载到存储中(许多表有数千行)。在打开几个页面并在 store 中有太多数据后,应用程序会变得非常慢,因为 ImmutableJS store 甚至可以包含数百万个条目。

如何从商店中“删除”某些内容,以免数据拖慢应用程序的速度?我知道这违反了它的主要原则,但是你还能怎么解决呢?

使用一个常见的网站,例如 jQuery,这将非常容易。每次刷新页面时,所有不必要的东西都会被垃圾收集。因此,一个页面有 2-3 千个条目是可以的,但是当打开一个新页面时,reducer 会加载新数据,但仍然会引用旧数据。

当然,我不想强​​迫用户重新加载页面。

【问题讨论】:

【参考方案1】:

三重检查这确实是内存膨胀,而不仅仅是不必要的重新渲染/重新计算

Redux 中的变异状态几乎总是一个真正的bad idea,因为它是库的一种先决条件。我的第一个问题是三重检查您是否确实遇到了由于内存膨胀而不是由于不必要的重新渲染或不必要的计算而导致的内存问题。我的意思是,如果您将大量数据保存在同一个地方,请确保您没有做任何导致 React 不必要地重新渲染事物或筛选过多数据的事情。

您可以通过司法使用reselect 库或使用其他类型的记忆来解决该问题,该记忆将以避免不必要的重新计算的方式从您的减速器中检索数据。同样,在更改单行时,请确保您不在整个列表中的 unnecessarily re-rendering every item。

摆脱对先前状态的引用

To get something to be garbage compiled in javascript, you just make sure that nothing is referencing it any longer.

如果您真的需要沿着这条路走下去,请务必不要让旧页面的数据保持“活跃”状态,因为 JavaScript 只会垃圾收集不再引用的内容。

Vanilla Redux 并没有在后台保留以前的状态。它所做的只是将当前状态保存在 let 变量中,然后在从将操作分派到根 reducer 获得新状态后更改其值(请参阅 source code)。

所以,我会确保我没有使用诸如 persists previous state 的 redux 开发工具之类的东西。

然后在reducer中,创建一个不以任何方式使用之前数据的新对象,而是返回一个带有新数据的新对象:

const data = (state, action) => 
  switch (action.type) 
    case 'RETRIEVE_DATA_SUCCESS':
      return action.payload;
    default:
      return state
  

我建议阅读 Chrome 的内存分析工具 here 以确保它正常工作。

【讨论】:

【参考方案2】:

我建议不要将整个数据保存在存储中,而是将它的指针保存到内存解决方案(本地存储、REDIS 等)。我会使用PouncDB 并将_rev 修订号仅存储在我的商店中。

【讨论】:

谢谢。关于在 Redux 中使用这项技术的材料,你有什么建议吗?我主要考虑将这些数据放在商店之外的某个地方,但是在干净的 JS 变量中,没有其他数据库,因此我对有关利弊的更多信息感兴趣。 不,我没有具体的东西,但它非常简单,你发出一个动作让我们说'SAVE_DATA',你可以有一个小的中间件来保存你的数据。 'GET_DATA' 也一样。对于异步操作,有很多解决方案,例如 sagas、redux-pack 等【参考方案3】:

上一篇文章关于 Immutablejs 保持旧数据“活着”的说法是错误的……嗯,从某种意义上说,它正在被描述。 Immutablejs 使用结构共享,这意味着它只会创建一个新节点及其子节点,而与旧数据共享 trie 的其余部分。如果您查看下图,您会发现右侧的绿色和黄色点是新数据,与旧数据共享其结构。

旧数据部分(右侧蓝点)可用于垃圾收集,只要您不在应用中的其他位置保留对它的引用强>。如果您想为您的应用添加时间旅行功能,保留参考资料非常有用。

如果您按照惯用的 Redux 深度克隆每个对象,那么如果没有 immutablejs 的结构共享,您将遇到更大的内存问题。所以没有看到一些代码,我怀疑这里的问题是不可变的。

您是否偶然使用了toJS()?如果你是,那是一项昂贵的操作,除非绝对必要,否则应该避免。它还会断开不可变实例的连接,您将失去结构共享的所有好处。这是我见过的常见错误。您无需像这样调用toJS() 即可访问这些值

const immVal = List([1,2,3]) console.log([...immVal.values()])

Lee Byron's talk (creator of immutablejs) 结构共享@23:40 和垃圾回收@24:50

【讨论】:

编辑了我的,因为剩余的引用不包括有关摆脱 Immutable.JS 的信息。但是,如果我在实践中真的错了,我会感到惊讶,但由于我无法证明,我将其删除。 经过一番思考后,我相信 OP 的主要问题是关于保留不需要的引用,但这是最定义不是不可变的错。听起来他们正在将新页面附加到 Redux 存储中,而不是仅使用新页面的数据重置减速器。如果他们随后对该数据运行过滤器,他们会在加载数百万个条目后发现速度变慢。【参考方案4】:

因为看到有人推荐了 PouchDB,所以希望大家在 reduxdb 上试用一下,我们已经在生产环境中使用了将近一年,没有发现任何性能问题。

【讨论】:

以上是关于Redux + ImmutableJS - 如何垃圾收集太大的存储?的主要内容,如果未能解决你的问题,请参考以下文章

combineReducers 破坏 Redux 状态并破坏多个 ImmutableJs 减速器

React-Native + Redux + ImmutableJS内存泄漏

ImmutableJS 映射到 javascript 对象

前端日记 -----简单记录一下自己浏览的前端文章

redux的笔记

Redux 中的 React ReRender