如何使用 React + Flux 快速渲染 >10000 个项目?
How to fast render >10000 items using React + Flux?
我想问一下在 React 中快速渲染 > 10000 项的正确方法是什么。
假设我想创建一个复选框列表,其中 包含超过 10000 个动态复选框项目。
我创建了一个包含所有项目的商店,它将用作复选框列表的状态。
当我点击任何复选框项目时,它会通过操作更新相应的项目,因此商店发生变化。
由于商店已更改,因此会触发复选框列表更新。
复选框列表更新其状态并再次呈现。
这里的问题是,如果我单击任何复选框项目,我必须等待 > 3 秒才能看到复选框被选中。我不希望这是因为只有 1 个复选框项目需要重新呈现。
我试图找到根本原因。 最耗时的部分在checkbox list render方法里面,和.map有关,创建Checkbox组件形成componentList..但实际上只有1个checkbox需要重新渲染。
以下是我的代码。
我使用 ReFlux 作为通量架构。
CheckboxListStore
商店将所有复选框项目存储为地图。 (名称作为键,状态 (true/false) 作为值)
const Reflux = require('reflux');
const Immutable = require('immutable');
const checkboxListAction = require('./CheckboxListAction');
let storage = Immutable.OrderedMap();
const CheckboxListStore = Reflux.createStore({
listenables: checkboxListAction,
onCreate: function (name) {
if (!storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
onCheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, true);
this.trigger(storage);
}
},
onUncheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
getStorage: function () {
return storage;
}
});
module.exports = CheckboxListStore;
CheckboxListAction
操作,创建、选中和取消选中提供名称的任何复选框项目。
const Reflux = require('reflux');
const CheckboxListAction = Reflux.createActions([
'create',
'check',
'uncheck'
]);
module.exports = CheckboxListAction;
复选框列表
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const CheckboxItem = require('./CheckboxItem');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxList = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
getInitialState: function () {
return {
storage: checkboxListStore.getStorage()
};
},
render: function () {
const {storage} = this.state;
const LiComponents = storage.map((state, name) => {
return (
<li key = {name}>
<CheckboxItem name = {name} />
</li>
);
}).toArray();
return (
<div className = 'checkbox-list'>
<div>
CheckBox List
</div>
<ul>
{LiComponents}
</ul>
</div>
);
},
onStoreChange: function (storage) {
this.setState({storage: storage});
}
});
module.exports = CheckboxList;
复选框项
在 onChange 回调中,我调用操作来更新项目。
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxItem = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function () {
const {name} = this.props;
return {
checked: checkboxListStore.getStorage().get(name)
};
},
onStoreChange: function (storage) {
const {name} = this.props;
this.setState({
checked: storage.get(name)
});
},
render: function () {
const {name} = this.props;
const {checked} = this.state;
return (
<div className = 'checkbox' style = {{background: checked ? 'green' : 'white'}} >
<span>{name}</span>
<input ref = 'checkboxElement' type = 'checkbox'
onChange = {this.handleChange}
checked = {checked}/>
</div>
);
},
handleChange: function () {
const {name} = this.props;
const checked = $(this.refs.checkboxElement).is(':checked');
if (checked) {
checkboxListAction.check(name);
} else {
checkboxListAction.uncheck(name);
}
}
});
module.exports = CheckboxItem;
您的渲染函数看起来比它需要的要复杂一些:
- 它首先生成一个 JSX 组件数组
- 然后转换应用 (jQuery?)
.toArray()
- 然后returns这个新生成的数组。
也许将您的渲染函数简化为这样的东西会有所帮助?
render: function () {
return (
<div className = 'checkbox-list'>
<div>
CheckBox List
</div>
<ul>
{this.state.storage.map((state, name) => {
return (
<li key = {name}>
<CheckboxItem name = {name} />
</li>
);
})}
</ul>
</div>
);
},
您可以采取以下几种方法:
- 不要渲染所有 10,000 个 - 只渲染基于面板大小和滚动位置的可见复选框(+几个),并处理滚动事件以更新可见子集(为此使用组件状态,而不是通量)。您需要以某种方式处理滚动条,通过手动渲染滚动条,或者通过在顶部和底部添加巨大的空 div 来替换您未渲染的复选框,使用普通浏览器滚动条更容易,这样滚动条位于正确的位置。这种方法允许您处理 100,000 个甚至一百万个复选框,并且第一次渲染和更新都很快。可能是首选的解决方案。这里有很多这种方法的例子:http://react.rocks/tag/InfiniteScroll
- 微优化 - 你可以做
storage.toArray().map(...)
(这样你就不会创建中间映射),或者更好的是,创建并清空数组然后做 storage.forEach(... ) 用 push 添加元素 - 快得多。但是 React diffing 算法仍然需要对 10000 个元素进行 diff,这永远不会很快,无论你编写生成元素的代码有多快。
- 以某种方式将你的大地图分成块,这样当你选中一个复选框时只有 1 个块发生变化。还以相同的方式(分成 CheckboxListChunks)或类似的方式拆分 React 组件。这样,只要每个块都有一个 PureComponent 类型的 componentShouldUpdate 函数,您就只需要重新渲染更改的块(可能 Reflux 会为您做这个?)。
- 远离基于 ImmutableJS 的通量,这样您就可以更好地控制什么时候发生变化(例如,您不必仅仅因为其中一个子项发生了变化而更新父复选框映射)。
将自定义的 shouldComponentUpdate 添加到 CheckboxList:
shouldComponentUpdate:function(nextProps, nextState) {
var storage = this.state.storage;
var nextStorage = nextState.storage;
if (storage.size !== nextStorage.size) return true;
// check item names match for each index:
return !storage.keySeq().equals(nextStorage.keySeq());
}
您真的需要每次check/uncheck都在您的存储中保存支票状态吗?
我最近遇到了和你一样的问题。只需在 CheckboxList 组件的状态中保存一个 checkedList 数组 [name1,name2 ...],并在每次 check/uncheck 一个项目时更改此 checkedList。当您想将检查状态保存到数据存储时,调用一个 Action.save() 并将 checkedList 传递给 store.
但是如果你真的需要每次都保存到数据存储check/uncheck,这个解决方案就无济于事了-_-。
顺便说一句,我放弃助焊剂...
我最终决定使用 mobservable 来解决我的问题。
我做了一个例子https://github.com/raymondsze/react-example
参见 https://github.com/raymondsze/react-example/tree/master/src/mobservable 的编码。
除了初始渲染之外,您还可以使用 Mobservable 显着提高大型集合的渲染速度。它通过自动应用横向加载避免在子项更改时不必要地重新渲染映射 10.000 个项目的父组件。请参阅此 blog 以获得深入的解释。
我想问一下在 React 中快速渲染 > 10000 项的正确方法是什么。
假设我想创建一个复选框列表,其中 包含超过 10000 个动态复选框项目。
我创建了一个包含所有项目的商店,它将用作复选框列表的状态。
当我点击任何复选框项目时,它会通过操作更新相应的项目,因此商店发生变化。
由于商店已更改,因此会触发复选框列表更新。
复选框列表更新其状态并再次呈现。
这里的问题是,如果我单击任何复选框项目,我必须等待 > 3 秒才能看到复选框被选中。我不希望这是因为只有 1 个复选框项目需要重新呈现。
我试图找到根本原因。 最耗时的部分在checkbox list render方法里面,和.map有关,创建Checkbox组件形成componentList..但实际上只有1个checkbox需要重新渲染。
以下是我的代码。 我使用 ReFlux 作为通量架构。
CheckboxListStore
商店将所有复选框项目存储为地图。 (名称作为键,状态 (true/false) 作为值)
const Reflux = require('reflux');
const Immutable = require('immutable');
const checkboxListAction = require('./CheckboxListAction');
let storage = Immutable.OrderedMap();
const CheckboxListStore = Reflux.createStore({
listenables: checkboxListAction,
onCreate: function (name) {
if (!storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
onCheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, true);
this.trigger(storage);
}
},
onUncheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
getStorage: function () {
return storage;
}
});
module.exports = CheckboxListStore;
CheckboxListAction
操作,创建、选中和取消选中提供名称的任何复选框项目。
const Reflux = require('reflux');
const CheckboxListAction = Reflux.createActions([
'create',
'check',
'uncheck'
]);
module.exports = CheckboxListAction;
复选框列表
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const CheckboxItem = require('./CheckboxItem');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxList = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
getInitialState: function () {
return {
storage: checkboxListStore.getStorage()
};
},
render: function () {
const {storage} = this.state;
const LiComponents = storage.map((state, name) => {
return (
<li key = {name}>
<CheckboxItem name = {name} />
</li>
);
}).toArray();
return (
<div className = 'checkbox-list'>
<div>
CheckBox List
</div>
<ul>
{LiComponents}
</ul>
</div>
);
},
onStoreChange: function (storage) {
this.setState({storage: storage});
}
});
module.exports = CheckboxList;
复选框项 在 onChange 回调中,我调用操作来更新项目。
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxItem = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function () {
const {name} = this.props;
return {
checked: checkboxListStore.getStorage().get(name)
};
},
onStoreChange: function (storage) {
const {name} = this.props;
this.setState({
checked: storage.get(name)
});
},
render: function () {
const {name} = this.props;
const {checked} = this.state;
return (
<div className = 'checkbox' style = {{background: checked ? 'green' : 'white'}} >
<span>{name}</span>
<input ref = 'checkboxElement' type = 'checkbox'
onChange = {this.handleChange}
checked = {checked}/>
</div>
);
},
handleChange: function () {
const {name} = this.props;
const checked = $(this.refs.checkboxElement).is(':checked');
if (checked) {
checkboxListAction.check(name);
} else {
checkboxListAction.uncheck(name);
}
}
});
module.exports = CheckboxItem;
您的渲染函数看起来比它需要的要复杂一些:
- 它首先生成一个 JSX 组件数组
- 然后转换应用 (jQuery?)
.toArray()
- 然后returns这个新生成的数组。
也许将您的渲染函数简化为这样的东西会有所帮助?
render: function () {
return (
<div className = 'checkbox-list'>
<div>
CheckBox List
</div>
<ul>
{this.state.storage.map((state, name) => {
return (
<li key = {name}>
<CheckboxItem name = {name} />
</li>
);
})}
</ul>
</div>
);
},
您可以采取以下几种方法:
- 不要渲染所有 10,000 个 - 只渲染基于面板大小和滚动位置的可见复选框(+几个),并处理滚动事件以更新可见子集(为此使用组件状态,而不是通量)。您需要以某种方式处理滚动条,通过手动渲染滚动条,或者通过在顶部和底部添加巨大的空 div 来替换您未渲染的复选框,使用普通浏览器滚动条更容易,这样滚动条位于正确的位置。这种方法允许您处理 100,000 个甚至一百万个复选框,并且第一次渲染和更新都很快。可能是首选的解决方案。这里有很多这种方法的例子:http://react.rocks/tag/InfiniteScroll
- 微优化 - 你可以做
storage.toArray().map(...)
(这样你就不会创建中间映射),或者更好的是,创建并清空数组然后做 storage.forEach(... ) 用 push 添加元素 - 快得多。但是 React diffing 算法仍然需要对 10000 个元素进行 diff,这永远不会很快,无论你编写生成元素的代码有多快。 - 以某种方式将你的大地图分成块,这样当你选中一个复选框时只有 1 个块发生变化。还以相同的方式(分成 CheckboxListChunks)或类似的方式拆分 React 组件。这样,只要每个块都有一个 PureComponent 类型的 componentShouldUpdate 函数,您就只需要重新渲染更改的块(可能 Reflux 会为您做这个?)。
- 远离基于 ImmutableJS 的通量,这样您就可以更好地控制什么时候发生变化(例如,您不必仅仅因为其中一个子项发生了变化而更新父复选框映射)。
将自定义的 shouldComponentUpdate 添加到 CheckboxList:
shouldComponentUpdate:function(nextProps, nextState) { var storage = this.state.storage; var nextStorage = nextState.storage; if (storage.size !== nextStorage.size) return true; // check item names match for each index: return !storage.keySeq().equals(nextStorage.keySeq()); }
您真的需要每次check/uncheck都在您的存储中保存支票状态吗?
我最近遇到了和你一样的问题。只需在 CheckboxList 组件的状态中保存一个 checkedList 数组 [name1,name2 ...],并在每次 check/uncheck 一个项目时更改此 checkedList。当您想将检查状态保存到数据存储时,调用一个 Action.save() 并将 checkedList 传递给 store.
但是如果你真的需要每次都保存到数据存储check/uncheck,这个解决方案就无济于事了-_-。
顺便说一句,我放弃助焊剂... 我最终决定使用 mobservable 来解决我的问题。 我做了一个例子https://github.com/raymondsze/react-example 参见 https://github.com/raymondsze/react-example/tree/master/src/mobservable 的编码。
除了初始渲染之外,您还可以使用 Mobservable 显着提高大型集合的渲染速度。它通过自动应用横向加载避免在子项更改时不必要地重新渲染映射 10.000 个项目的父组件。请参阅此 blog 以获得深入的解释。