Инъекция React JS в приложение на Angular JS или борьба за производительность

Дорогие Хабролюбители, всем привет! Не откроем Америку если скажем, что существуют сотни плагинов и библиотек, которые облегчают специализированные задачи, связанные с построением современных web интерфейсов. Angular один из них, про его производительность писалось много и в большинстве случаев даются рекомендации чего не нужно делать, чтобы все было хорошо.Основное аргумент, сторонников — медленно работают неправильные приложения, а вот правильные не должны содержать более 2000–3000 элементов. Если содержит больше значит что-то не так. См. например http://iantonov.me/page/angularjs-osnovy.

Аргумент в общем вполне здравый, но всегда есть ситуации, когда нужно написать «неправильное» web приложение потому, что такие требования. В этой статье мы решили рассказать, как раз про такую задачу, и как мы ее решили. На наш взгляд статья будет полезна в большей степени профессиональным веб разработчикам. Итак, наша задача была сделать календарь для системы бронирования для одного спортивного клуба. Календарь отображает семь 12 — часовых блоков, каждый день из которых разделен на 15 минутные интервалы. В блоке может быть от 2-х до 10 DOM элементов. Вроде ничего не предвещало беды, верхняя граница ~3000.

0258121fe72e49d7b086790c3f8ea5c0.png

Ну что-же начинаем писать:

….
Готово! Но! Data-binding всего этого занимал примерно 2–3 секунды. Кстати, тут может встать вопрос, как вообще измерить сколько он занимает? Попытка сделать это с помощью профайлера напрямую не дает ответа, в профайлере трудно понять, что относится именно к data-binding листа, а что добавляет routing и прочее.

Поэтому мы сделали довольно просто:

$timeout (function () { $scope.days = days; var now = new Date (); $timeout (function () { console.log (new Date — now); $scope.apply () }, 0); }, 100); Первый timeout нужен, чтобы отработали все другие скрипты, чтобы не нарушать точность измерения, собственно число 100(ms) подбирается экспериментально. Второй можно смело делать 0, т.к. как только браузер получит первую передышку, он сразу выполнит внутренний handler $timeout-a, что собственно и будет обозначать, что data-binding завершен.

С точки зрения отзывчивости пользовательского интерфейса 2 — 3 секунды довольно большие значения, как же решать эту проблему?

Первым делом конечно еще и еще раз перечитать tech.small-improvements.com/2013/09/10/angularjs-performance-with-large-lists/ и конечно же Optimizing AngularJS: 1200ms to 35ms. В последней статье результат кстати выглядит особенно впечатляющее. Но результат достигается виртуализацией (т.е. тем что отрисовывается только маленькая отображаемая часть) и кэшированием. Виртуализация — хорошее решение, но только когда виртуализуемый контент достаточно несложен. В противном случае проявляются очень неприятные тормоза при скроллинге страницы, которые нивелируют весь эффект виртуализации.

Кстати, говоря о виртуализации, для отображения табличных данных есть замечательный элемент ngGrid, который может отображать миллионы (проверьте:)) элементов. Но разгадка здесь конечно опять — виртуализация (а иначе почему все строки в ngGrid одинаковой высоты).

Итак, перепробовав множество вариантов, в конце концов приходим к ReactJS. По заявлениям разработчиков, это наиболее быстрый framework для data-binding (и теперь я склонен согласиться что это так). Хотя, например, на сайте Vue утверждается иное, мой собственный тест показал превосходство React.

Итак react.

На Хабре уже довольно много статей посвящённых этому framework-у. Для начинающих я бы посоветовал:

а также замечательную статью

На Хабре есть ее перевод на русский язык.

Секрет ReactJS в том, что он оперирует виртуальным DOM, благодаря чему может минимизировать количество действий, необходимых для отрисовки представления. Неплохая статья об этом здесь.

Код для отображения представления на ReactJS выглядит следующим образом:

var Hello = React.createClass ({ render: function () { return React.DOM.div ({}, 'Hello ' + this.props.name); } }); React.renderComponent (Hello ({name: 'World'}), document.body); Здесь, первая часть определяет компонент, а строчка «React.renderComponent (Hello ({name: 'World'}), document.body)», отрисовывает его.API React.DOM — достаточно удобен, однако после шаблонов Angualr и Knockout, генерация DOM с помощью Javascript выглядит немного архаично, и может показаться слишком трудоемким занятием. Для решения этого Facebook предлагает «In-browser JSX Transform».

Встречая в браузере