在 React 中构建一个大型多选而不是真的很慢

Building a large multiselect in React without it being really slow

我正在构建的应用程序需要相当多的多选。它在用户界面中工作,因为我正在使用 this library 让人们搜索选项,但我无法在合理的时间内在 React 中呈现它。

Link to JSFiddle.

我做了一个更简单的版本来显示同样的问题。

它包含一些设置代码,创建一个包含 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 在 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)

jsfiddle

它在我的机器上与你的 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>

jsfiddle