Я принял пример TodoList, чтобы отразить мою проблему, но, очевидно, мой код в реальном мире более сложный.
У меня есть такой псевдокод.
var Todo = React.createClass({
mixins: [PureRenderMixin],
............
}
var TodosContainer = React.createClass({
mixins: [PureRenderMixin],
renderTodo: function(todo) {
return <Todo key={todo.id} todoData={todo} x={this.props.x} y={this.props.y} .../>;
},
render: function() {
var todos = this.props.todos.map(this.renderTodo)
return (
<ReactCSSTransitionGroup transitionName="transition-todo">
{todos}
</ReactCSSTransitionGroup>,
);
}
});
Все мои данные неизменяемы, и PureRenderMixin используется надлежащим образом, и все работает нормально. Когда данные Todo изменяются, повторно отображается только родительский и отредактированный todo.
Проблема в том, что в какой-то момент мой список растет, когда пользователь прокручивается. И когда обновляется одно Todo, требуется все больше и больше времени для рендеринга родителя, вызывайте shouldComponentUpdate
на всех todos, а затем визуализируйте одиночное todo.
Как вы можете видеть, компонент Todo имеет другой компонент, чем данные Todo. Это данные, которые необходимы для рендеринга всеми todos и являются общедоступными (например, мы могли бы представить там "displayMode" для todos). Наличие многих свойств делает shouldComponentUpdate
немного медленнее.
Кроме того, использование ReactCSSTransitionGroup
также немного замедляется, так как ReactCSSTransitionGroup
должен отображать себя и ReactCSSTransitionGroupChild
еще до вызова shouldComponentUpdate
todos. React.addons.Perf
показывает, что ReactCSSTransitionGroup > ReactCSSTransitionGroupChild
рендеринг является временем, потраченным впустую для каждого элемента списка.
Итак, насколько я знаю, я использую PureRenderMixin
, но с большим списком этого может быть недостаточно. Я по-прежнему получаю не очень плохие представления, но хотел бы знать, есть ли простые способы оптимизации моих рендерингов.
Любая идея?
Edit
До сих пор мой большой список был разбит на страницы, поэтому вместо большого списка элементов теперь я разбил этот большой список в списке страниц. Это позволяет улучшить производительность, поскольку каждая страница теперь может реализовать shouldComponentUpdate
. Теперь, когда элемент изменяется на странице, React должен вызывать основную функцию рендеринга, которая выполняет итерацию на странице, и вызывать функцию рендеринга только с одной страницы, что делает работу с меньшим количеством итераций.
Однако моя производительность рендера по-прежнему линейна для номера страницы (O (n)). Так что, если у меня есть тысячи страниц, это все равно та же проблема:) В моем случае это вряд ли произойдет, но меня все еще интересует лучшее решение.
Я уверен, что можно добиться производительности вывода O (log (n)), где n - количество элементов (или страниц), путем разделения большого списка на дерево (например, постоянная структура данных) и где каждый node имеет возможность короткого замыкания вычисления с помощью shouldComponentUpdate
Да, я думаю о том, что похоже на постоянные структуры данных, такие как Vector в Scala или Clojure:
Однако меня беспокоит React, потому что, насколько я знаю, может возникнуть необходимость создавать промежуточные dom-узлы при рендеринге внутренних узлов дерева. Это может быть проблемой в соответствии с usecase (и может быть решена в будущих версиях React)
Также, поскольку мы используем Javascript, мне интересно, поддерживает ли Immutable-JS это и делает доступными "внутренние узлы". См.: https://github.com/facebook/immutable-js/issues/541
Изменить: полезная ссылка с моими экспериментами: Может ли приложение React-Redux действительно масштабироваться, а также, скажем, Backbone? Даже с повторным выбором. На мобильном телефоне