在 React 中构建一个大型多选而不是真的很慢
Building a large multiselect in React without it being really slow
我正在构建的应用程序需要相当多的多选。它在用户界面中工作,因为我正在使用 this library 让人们搜索选项,但我无法在合理的时间内在 React 中呈现它。
我做了一个更简单的版本来显示同样的问题。
它包含一些设置代码,创建一个包含 500 个项目的列表。它还列出了默认情况下应选择的项目。现在我将它设置为 x%1
,所以默认情况下每个选项都是打开的,因为这似乎是瓶颈。我还启动了 startTime
变量来跟踪事物。
var list = [];
var defaultList = [];
for (var x = 0; x < 500; x += 1) {
list.push(x);
if (x%1 === 0) {
defaultList.push(x)
}
}
var startTime=new Date().getTime();
然后我们将它传递到一个带有 ReactDom.render()
的组件中,它有一个回调来提醒渲染后已经过了多少毫秒:
ReactDOM.render(
<Hello
list={list}
defaultList={defaultList}
/>,
document.getElementById('container'),
()=>{
alert(new Date().getTime() - startTime)
}
);
最后,组件本身,它只是对 list
中的所有选项进行简单的多选,并默认检查 defaultList
:
中的所有选项
var Hello = React.createClass({
render: function() {
return (
<select defaultValue={this.props.defaultList} multiple>
{this.props.list.map(item => {
return (<option value={item} key={item}>{item}</option>)
})}
</select>
)
}
});
运行 我计算机上的这个 fiddle 会产生一个警报,显示 超过 1000 毫秒 来呈现此组件。如果我将默认列表的限定符从 x%1
更改为 x%50
,那么有 10 个选定项目,它只需要 76 毫秒。
起初,我假设瓶颈只是构建 500 个 option
元素,但似乎速度下降实际上是因为使用了大量默认值。
所以我想我的问题是:
- 有谁知道一种方法可以更快地渲染相同的结果?
- 关于 React 性能,我是否缺少一些见解?
- 为什么添加默认值会增加这么多开销?
谢谢。
请注意,我 知道 React 在 JSFiddle 中会 运行 变慢,并且在生产中我应该使用 React 的生产版本,和,如果我没有列出 500 项长的列表,就不会花这么长时间。这些不是我要找的答案。
编辑:看起来 React 可能一次将每个 option
标记为 selected
,导致 500 次重新渲染。这或许可以解释发生了什么——这是 React 中的错误吗?我在 Chrome 开发人员工具中看到 'Forced synchronous layout is a possible performance bottleneck.' 警告。
您看到的是 DOM 的第一个渲染速度很慢。拿这个代码:
let html = '<select multiple>'
for (var x = 0; x < 500; x += 1) {
html += `<option value=${x} selected>${x}</option>`
}
html += '</select>'
var startTime=new Date().getTime();
document.getElementById('container').innerHTML = html;
alert(new Date().getTime() - startTime)
它在我的机器上与你的 react-code 执行相同(Chrome 47,Windows 10),大约 5000 毫秒。
如果我使用 Microsoft Edge,时间从 5000 毫秒变为 231 毫秒(反应)和 12 毫秒(DOM-manipulation)。这里 Chrome DOM 很慢(与 Edge 相比)并且 option-elements 最终必须渲染为 DOM。更新可能会更快。
我已经编写了一些代码来执行相同的操作,但不是 <select>
字段。这是一个无序列表,列表项具有点击处理程序。性能大约提高 10 倍(Chrome ~500ms,Edge ~300ms)。
class Select extends React.Component {
constructor (props) {
super(props)
this.state = {}
this.state.selected = props.selected
}
handleClick () {
this.setState({selected: !this.state.selected})
}
render () {
const val = this.props.value
const selected = this.state.selected
return (
<li onClick={this.handleClick.bind(this)} className={selected ? 'selected' : ''}>
{val}
</li>
)
}
}
const Hello = ({list, defaultList}) =>
<ul>
{list.map(item =>
<Select key={item} value={item} selected={defaultList.indexOf(item) !== -1}/>)}
</ul>
我正在构建的应用程序需要相当多的多选。它在用户界面中工作,因为我正在使用 this library 让人们搜索选项,但我无法在合理的时间内在 React 中呈现它。
我做了一个更简单的版本来显示同样的问题。
它包含一些设置代码,创建一个包含 500 个项目的列表。它还列出了默认情况下应选择的项目。现在我将它设置为 x%1
,所以默认情况下每个选项都是打开的,因为这似乎是瓶颈。我还启动了 startTime
变量来跟踪事物。
var list = [];
var defaultList = [];
for (var x = 0; x < 500; x += 1) {
list.push(x);
if (x%1 === 0) {
defaultList.push(x)
}
}
var startTime=new Date().getTime();
然后我们将它传递到一个带有 ReactDom.render()
的组件中,它有一个回调来提醒渲染后已经过了多少毫秒:
ReactDOM.render(
<Hello
list={list}
defaultList={defaultList}
/>,
document.getElementById('container'),
()=>{
alert(new Date().getTime() - startTime)
}
);
最后,组件本身,它只是对 list
中的所有选项进行简单的多选,并默认检查 defaultList
:
var Hello = React.createClass({
render: function() {
return (
<select defaultValue={this.props.defaultList} multiple>
{this.props.list.map(item => {
return (<option value={item} key={item}>{item}</option>)
})}
</select>
)
}
});
运行 我计算机上的这个 fiddle 会产生一个警报,显示 超过 1000 毫秒 来呈现此组件。如果我将默认列表的限定符从 x%1
更改为 x%50
,那么有 10 个选定项目,它只需要 76 毫秒。
起初,我假设瓶颈只是构建 500 个 option
元素,但似乎速度下降实际上是因为使用了大量默认值。
所以我想我的问题是:
- 有谁知道一种方法可以更快地渲染相同的结果?
- 关于 React 性能,我是否缺少一些见解?
- 为什么添加默认值会增加这么多开销?
谢谢。
请注意,我 知道 React 在 JSFiddle 中会 运行 变慢,并且在生产中我应该使用 React 的生产版本,和,如果我没有列出 500 项长的列表,就不会花这么长时间。这些不是我要找的答案。
编辑:看起来 React 可能一次将每个 option
标记为 selected
,导致 500 次重新渲染。这或许可以解释发生了什么——这是 React 中的错误吗?我在 Chrome 开发人员工具中看到 'Forced synchronous layout is a possible performance bottleneck.' 警告。
您看到的是 DOM 的第一个渲染速度很慢。拿这个代码:
let html = '<select multiple>'
for (var x = 0; x < 500; x += 1) {
html += `<option value=${x} selected>${x}</option>`
}
html += '</select>'
var startTime=new Date().getTime();
document.getElementById('container').innerHTML = html;
alert(new Date().getTime() - startTime)
它在我的机器上与你的 react-code 执行相同(Chrome 47,Windows 10),大约 5000 毫秒。
如果我使用 Microsoft Edge,时间从 5000 毫秒变为 231 毫秒(反应)和 12 毫秒(DOM-manipulation)。这里 Chrome DOM 很慢(与 Edge 相比)并且 option-elements 最终必须渲染为 DOM。更新可能会更快。
我已经编写了一些代码来执行相同的操作,但不是 <select>
字段。这是一个无序列表,列表项具有点击处理程序。性能大约提高 10 倍(Chrome ~500ms,Edge ~300ms)。
class Select extends React.Component {
constructor (props) {
super(props)
this.state = {}
this.state.selected = props.selected
}
handleClick () {
this.setState({selected: !this.state.selected})
}
render () {
const val = this.props.value
const selected = this.state.selected
return (
<li onClick={this.handleClick.bind(this)} className={selected ? 'selected' : ''}>
{val}
</li>
)
}
}
const Hello = ({list, defaultList}) =>
<ul>
{list.map(item =>
<Select key={item} value={item} selected={defaultList.indexOf(item) !== -1}/>)}
</ul>