一个React-Redux应用程序可以真正的扩展,比如Backbone? 即使重新select。 在移动
在Redux中,每次对商店进行更改都会触发所有连接组件的notify
。 这对于开发人员来说是非常简单的,但是如果你有一个N个连接组件的应用程序,而且N很大呢?
即使与组件无关,每次对存储的更改仍会对商店的reselect
path运行带有简单===
testing的shouldComponentUpdate
。 这很快,对吗? 当然,也许一次。 但是N次, 每一次改变? devise中的这一根本性变化使我怀疑Redux的真正可伸缩性。
作为进一步的优化,可以使用_.debounce
批量处理所有的notify
。 即便如此,对每个商店进行N ===
testing都会发生变化, 并处理其他逻辑,例如查看逻辑,似乎是达到目的的一种手段。
我正在与一个拥有数百万用户的健康和健身社交移动Web混合应用程序,并正在从Backbone转换到Redux 。 在这个应用程序中,用户会看到一个可滑动的界面,允许他们在不同的视图堆栈之间导航,类似于Snapchat,除了每个堆栈都有无限的深度。 在最stream行的视图中,无尽的滚动器可以有效地处理加载,渲染,附加和分离Feed等项目。 对于参与的用户来说,滚动成百上千个post,然后input用户的提要,然后input另一个用户的提要等是很常见的。即使进行了大量优化,连接组件的数量也可能变得非常大。
另一方面,Backbone的devise允许每个视图精确地聆听影响它的模型,将N减less到一个常量。
我是否错过了一些东西,或者Redux从根本上来说是一个大型应用程序的缺陷?
这不是Redux恕我直言的固有问题。
顺便说一句,不要试图同时渲染10万个组件,你应该尝试用一个类似react-infinite或类似的库来伪造它,并且只渲染列表中的可见(或接近)项。 即使你成功地渲染和更新了一个100k的列表,它仍然没有性能,并且需要大量的内存。 这里有一些LinkedIn的build议
这个anwser会认为你仍然尝试在你的DOM中渲染10万个可更新的项目,并且你不希望每一次更改都调用100k个监听器( store.subscribe()
)。
2所学校
在function上开发UI应用程序时,基本上有两个select:
总是从最顶层渲染
它运作良好,但涉及更多的样板。 这不完全是build议的Redux方式,但可以实现,有一些缺点 。 请注意,即使您设法拥有一个redux连接,您仍然必须在许多地方调用很多shouldComponentUpdate
。 如果你有一个无限的视图堆栈(如recursion),你将不得不渲染所有的中间视图虚拟的dom和shouldComponentUpdate
将被调用的很多人。 所以即使你有一个单一的连接,这个效率也不是很高。
如果您不打算使用React生命周期方法,但只使用纯渲染函数,那么您应该考虑其他类似的选项,只关注该工作,如deku (可用于Redux)
根据我自己的经验,使用React在老的移动设备(如我的Nexus4)上做得不够好,特别是如果您将文本input链接到您的primefaces状态。
将数据连接到子组件
这就是使用connect
所反应的反应 。 所以当状态发生变化时,它只和一个更深的孩子有关,你只渲染那个孩子,而不必象上下文提供者(redux / intl / custom …)或者主应用程序布局那样渲染顶层组件。 您还避免在其他子shouldComponentUpdate
上调用shouldComponentUpdate
,因为它已经被放入了侦听器。 调用大量非常快速的侦听器可能会比渲染每个中间响应组件的速度更快,并且还允许减less很多道具传递的样板,所以对于我来说,与React一起使用时很有意义。
还要注意,身份比较非常快,您可以在每次更改时轻松完成许多操作。 记住Angular的肮脏的检查:有些人确实设法build立真正的应用程序! 而身份比较要快得多。
了解你的问题
我不确定是否完全理解了所有的问题,但是我知道你有100k条目的意见,你不知道是否应该使用所有这些100k条目connect
,因为在每一个改变中调用10万个听众似乎代价很高。
这个问题看起来与使用UI进行函数式编程的本质有关:列表被更新了,所以你必须重新渲染列表,但是不幸的是它是一个很长的列表,而且看起来效率不高……有了Backbone,你可以破解一些东西只呈现给孩子。 即使你使用React渲染那个孩子,你也会以一种必要的方式触发渲染,而不是只声明“列表何时改变,重新渲染”。
解决你的问题
很明显,连接100k列表项似乎很方便,但是由于调用了100k个react-redux监听器,即使它们很快,也不是高性能的。
现在,如果连接100k个项目的大列表,而不是单个项目,则只能调用一个react-redux监听器,然后必须以高效的方式呈现该列表。
天真的解决scheme
迭代100k项目来呈现它们,导致99999个项目在shouldComponentUpdate
返回false,并重新渲染一个:
list.map(item => this.renderItem(item))
性能解决scheme1:自定义connect
+存储增强器
React-Redux的connect
方法只是一个高级组件 (HOC),它将数据注入到被包装的组件中。 为此,它为每个连接的组件注册一个store.subscribe(...)
侦听器。
如果你想连接一个单一列表的100k项目,这是一个值得优化的应用程序的关键path。 而不是使用默认的connect
你可以build立自己的一个。
- 存储增强器
公开一个额外的方法store.subscribeItem(itemId,listener)
将dispatch
包裹起来,以便每当与某个项目相关的操作被调度时,就调用该项目的注册监听者。
这个实现的一个很好的灵感来源可以是批量批量订阅 。
- 自定义连接
使用API创build高阶组件:
Item = connectItem(Item)
HOC可以期望一个itemId
属性。 它可以使用React上下文中的Redux增强存储,然后注册它的监听器: store.subscribeItem(itemId,callback)
。 原始connect
的源代码可以作为基础的灵感。
- 如果项目改变,HOC只会触发重新渲染
相关的答案: https : //stackoverflow.com/a/34991164/82609
相关react-redux问题: https : //github.com/rackt/react-redux/issues/269
高性能解决scheme2:vector尝试
一个更高性能的方法会考虑使用一个持久的数据结构,如向量trie :
如果你将你的100k项列表表示为trie,那么每个中间节点都有可能更快地将渲染短路,这可以避免儿童中的很多shouldComponentUpdate
。
这个技术可以和ImmutableJS一起使用,你可以在ImmutableJS中find一些实验: React性能:使用PureRenderMixin渲染大型列表它有一些缺点,但是像ImmutableJs这样的库还没有公开/稳定的API来做这个( issue ),和我的解决scheme污染DOM一些无用的中间节点( 问题 )。
这里是一个JsFiddle演示如何ImmutableJS列表的100K项目可以有效地呈现。 最初的渲染是相当长的(但我猜你没有初始化你的应用程序100k项目!),但是你可以注意到每个更新只导致less量的shouldComponentUpdate
。 在我的例子中,我只是每秒钟更新第一个项目,而且即使列表中有100k个项目,它也只需要110个调用shouldComponentUpdate
东西,这个更容易被接受! 🙂
编辑 :似乎ImmutableJS是不是很好,以保持其不可变的结构上的一些操作,如插入/删除随机索引项目。 这是一个JsFiddle ,根据列表上的操作演示你可以预期的性能。 令人惊讶的是,如果你想在一个大列表的末尾追加许多项目,多次调用list.push(value)
似乎比调用list.concat(values)
更多地保存树结构。
顺便说一下,它被logging在列表修改边缘是有效的。 我不认为这些在给定索引处添加/删除的不良performance与我的技术有关,而与底层的ImmutableJs List实现有关。
列出Deque的实现方式,从结尾(push,pop)和开始(unshift,shift)两方面进行高效的添加和删除。
这可能是一个比你想要的更普遍的答案,但一般来说:
- Redux文档中的build议是在组件层次结构中连接相当高的React组件。 看这部分。 。 这样可以保持连接数量的可pipe理性,然后您可以将更新后的道具传递给子组件。
- React的部分function和可伸缩性来自于避免渲染不可见的组件。 例如,不是在DOM元素上设置一个
invisible
类,而是在React中,我们根本不渲染组件。 由于虚拟DOM差异化过程优化了低级别的DOM交互,所以还没有改变的组件的渲染也不是问题。