想象一下包含100个项目的列表。有一个组件显示所有这些组件。每秒几次,一个(随机)项目应该移动到一个新的位置。 React天真的方法是通过构造虚拟树来重新渲染整个组件,然后使用先前的副本对其进行区分,然后对DOM进行修补。问题是制作虚拟树并创建差异需要花费时间(在我的情况下大约需要50毫秒)。
React有没有办法跳过虚拟树的创建并计算差异?如下所示:shouldComponentUpdate
将返回false
;然后手动将一个Node
从DOM中移除,并插入另一个位置。
更新
关于this video,React中存在最糟糕的情况。当您只更新其中100个项目中的一个项目时。问题是如何以最快的方式更新DOM (不分散100个项目)?
问题This is the demo和this is the code。
答案 0 :(得分:1)
您只需访问componentDidMount
中的DOM。
但是因为你遇到了性能问题。我会尝试让你的孩子渲染功能"纯粹"。你可以使用PureRenderMixin,但由于mixin可能不在你身边,你可以
import shallowEqual from 'react/lib/shallowEqual';
然后在你的shouldComponentUpdate
函数中执行
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
}
另请考虑使用Immutable-JS将100个项目保存为Map,因为这样可以最大限度地减少复制或缓存数据的需要。
答案 1 :(得分:1)
我在一个特定情况下使用的方法(不适用于每种情况)只是为每个节点设置一些位置,然后用CSS设置它的位置。
类似的东西:
<div class="parent">
<div data-position="3"></div>
<div data-position="1"></div>
<div data-position="2"></div>
</div>
虽然position属性根据某些排序顺序而改变,但节点的DOM位置保持不变。问题是您需要使用CSS设置每个节点的可视位置,如下所示:
.parent {position: relative}
.parent div {
position: absolute;
height: <something>px;
width: 100%;
}
.parent div[data-position="1"] { top: 0; }
.parent div[data-position="2"] { top: <something>px; }
.parent div[data-position="3"] { top: <something * 2>px; }
这样可以避免浏览器执行DOM重新计算,但需要重新计算渲染树。但是,如果您每秒多次更改元素的位置,则只进行一次重新计算(因为只要您没有执行任何re-layout,浏览器就会尝试应用批量更新。)
CSS&#39; top&#39;可以使用Less / Sass之类的工具或使用普通JS生成属性,甚至可以通过组件中的render方法生成属性,在每次传递中设置每个节点的样式:它只具有预设的高度那将被你正在迭代的索引乘以。
很抱歉,如果这不是您期望的那种答案,但我使用了这种方法并为我提供了数百种元素。
答案 2 :(得分:0)
一种技术是不重新创建已经存在的节点,即缓存。这里有一个demo,其中包含1,000个节点,每秒都会从数组中删除一个随机项,并在随机位置添加一个项。使用反应的开发版本,我的笔记本电脑上的更新需要10毫秒。
我说了很多,但我有一个大显示器,我一次只能看到70个这样的物品而且它太多了。这里最好的答案是不从性能和ux角度渲染1,000个项目。
假设您有一个具有id属性的对象数组,以及其他一些东西。
class Foo {
constructor(){
this._nodeCache = {};
}
renderItem(item){
return <div key={item.id}>{item.text}</div>;
}
renderOrGetFromCache(id, item){
if (this._nodeCache[id]) {
return this._nodeCache[id];
}
this._nodeCache[id] = this.renderItem(item);
return this._nodeCache[id];
}
render(){
return (
<div>
{this.props.items.map((item) => this.renderOrGetFromCache(item.id, item))}
</div>
);
}
}
那么这与你以前的做法有何不同?
在和解之前看起来像:
现在看起来像:
===
检查)吗?
你仍在构建虚拟dom,但我们正在谈论渲染()的几分之一毫秒,以及一个非常快速的协调,甚至没有浅等于。
还需要注意的是此功能的签名:
renderOrGetFromCache(id, item)
第一个参数取代了我们的shouldComponentUpdate,这意味着它需要唯一地标识一个值。如果您碰巧更改了对象的.text属性,但ID相同,则无法更新。要解决此问题,您可以更改传递给renderOrGetFromCache的内容。这样,它会对呈现的项进行正常的差异化,因为键是相同的,但所有其他节点仍然是===
项检查。
这里的主要交易是您需要手动清理。按原样,此代码将泄漏内存。如果某个项目不再呈现或已更改,则需要将其从_nodeCache中删除。你如何做到这一点取决于你的数据如何随着时间的推移而变化,并且因为这是一个性能问题,所以没有普遍的“最佳”数据。答案。