Только React.js, только хардкор (aka долой Angular и Knockout)!

Последние годы только ленивый не писал о javascript-фреймворках. AngularJS/KnockoutJS/Backbone/Ember — выбирай на вкус :) Но, озадачившись выбором фреймворка для небольшого веб-приложения с год назад, оказалось, что серебряной пули в мире js все еще не придумали.
Backbone и Ember слишком низкоуровневы и многословны, Knockout расстраивает постоянными конвертациями данных в observable, AngularJS.. да, почему бы не попробовать Angular, подумал я тогда. Энгуляр обладал приятным синтаксисом, структура Контроллеров была более менее близка и понятна и всё шло хорошо, пока.. пока мы не уперлись в довольно стандартную для Angular проблему произодительности.

Стэковерфлоу переполнен ответами на тему angular vs. performance, и лучше всего ситуацию проиллюстрируют два ответа: детальнейшее описание работы dirty-checking в Angular, сводящееся к тому, что если на странице у вас меньше 2000 элементов, то всё будет отлично (а уж 2000 элементов должно быть достаточно для каждого!). И следующий за ним контрпример, что в случае больших таблиц 2000 элементов набегают очень быстро.
В нашем случае у нас было много выпадающих списков, вроде таких: с большим количеством выпадающих элементов, значения которых были привязаны к модели. Заветные 2К элементов набежали очень быстро, и при изменении модели всё нереально тормозило (двух-трехсекундный лаг в браузере нельзя было не заметить).

В тот момент проблему мы решили низкоуровневой «затычкой» — шаблон этого «высоконагруженного» участка начали обрабатывать простейшим шаблонизатором Render.Js‘ом (или mustache‘ом, или handlebars‘ом.. да какая, в общем-то, разница? :)) и все пошло нормально, но осадочек остался…

И вот, наткнувшись на просторах интернета на любопытное видео о клиентском javascript-фреймворке React.JS, разработанный фейсбуком и делающем упор на производительности, я не мог не открыть проектик годовой давности и не попробовать переверстать проблемную область на React.

В процессе перехода React показал себя исключительно хорошо: производительность действительно на отличном уровне. Сказывается то, что «шаблонизатор» у React — это нативный javascript. А html-подобный синтаксис (такой формат у Реакта называется JSX) конвертируется в нативные вызовы при первом обращении (в production же конвертацию, безусловно, следует делать на сервере средствами Node.js).
А вот и пример шаблона в React:

             <div>
                {navigation}
                <div>
                    {activeFilters}
                    <Filters filters1={this.state.filters1} filters2={this.state.filters2} />
                </div>
                <div>
                {this.state.ViewModel.Tree.Items.length <= 0?"No results":this.state.ViewModel.Tree.Items.map(function(item) {
                    return <div class="treeCell">
                                        <a href={'#/' + item.Id}>
                                    <span>{item.Title} <b>[{item.ChildrenCount}]</b>
                                        <br />
                                        <img src="{item.Image}" class="treeCellImage" />
                                    </span>
                                </a>
                            </div>;
                })}
                </div>
            </div>

Выглядит весьма хардкорно, особенно часть, верстающая повторяющиеся элементы:


{this.state.ViewModel.Tree.Items.length <= 0?"No results":this.state.ViewModel.Tree.Items.map(function(item) { [/javascript] Что как-бы еще раз напоминает, JSX - это обычный яваскрипт. Использование виртуального DOM, за счет которого и происходит отслеживание изменений в сгенерированном html, даёт огромный прирост в производительности. Списки с 3000 элементов, которые были камнем преткновения в Angular отработали без малейших проблем. Любопытной задачей при использовании React стало взаимодействие со сторонними контролами (например, jQuery.UI или подобными). Такие контролы веб-разработчики используют каждый день, и без взаимодействия с ними нормальное внедрение js-фреймворка просто немыслимо. В React для подобных взаимодействий напрямую с DOM и сторонними компонентами существуют коллбэки, которые вызываются в случае их объявления в контроле:

  • componentDidMount — вызывается по окончании первого успешного рендеринга компонента. К этому моменту DOM полностью сформирован и с ним можно работать.
  • componentDidUpdate — вызывается по окончании рендеринга, если свойства (props) или состояние (state) компонента изменилось.

При желании о цикле жизни компонентов Реакта можно прочитать подробнее. В моем маленьком примере интеграция с msDropDown заключалась в объявлении метода:

        componentDidUpdate: function( prevProps,  prevState,  rootNode) {
            $(rootNode).find('select').msDropDown();
        },

Ну и еще один неоспоримый плюс React.JS — простота внедрения и отсутствие необходимости переписывать все веб-приложение целиком, чтобы начать его использовать. Если Энгуляр требует поклонения религии Контроллеров, Роутеров, Скоупа и прочей инъекции зависимостей с первых же минут, то Реакт упирает на выделение возможных вещей в отдельные контролы-компоненты (что видно даже на моем мини-примере), и внедрять такие контрольчики можно с легкостью в уже работающий проект малыми дозами.

Опубликовать в Facebook
Опубликовать в Google Plus

9 комментариев

  1. Я ответил комментарием к исходной статье об IML :)

    Мне кажется IML в данном примере схожим с WebForms, он предлагает клиентские события, в том числе и отрисовку шаблона, отрабатывать на сервере.
    Такой подход cработает для многих приложений и часто является наиболее простым. Но ReactJS и AngularJS как раз и создавались для случаев, когда необходимо сложное динамическое клиентское приложение.

    И тут уйти от JS в сторону С# можно только используя что-то вроде Script#, SharpKit или похожего. Но пока использование js напрямую мне кажется всё-таки предпочтительным.

  2. или mustache‘ом, или handlebars‘ом.. да какая, в общем-то, разница? :)

    Handlebars быстрее, да и mustaches в IE 8 ( надо запускать xp, а не режим совместимости ) при вставки с выше 100 строк в таблице очень притормаживате, а то и зависает.

    Ответы про IML

    ReactJS и AngularJS как раз и создавались для случаев, когда необходимо сложное динамическое клиентское приложение.

    хотелось бы увидеть ( прочитать ) тот критерий, которые делает приложение сложным.

    уйти от JS в сторону С# можно только используя что-то вроде Script#, SharpKit или похожего. Но пока использование js напрямую мне кажется всё-таки предпочтительным

    Основная прелесть IML в том, что позволяет уйти без использования каких то дополнительных инструментов.

  3. хотелось бы увидеть ( прочитать ) тот критерий, которые делает приложение сложным.

    Конечно, однозначный критерий сформулировать сложно. Я бы назвал «сложными» приложения с большим количеством клиентской логики, требующие наличия «состояния» (state) на клиенте.
    Например, многокритериальные «калькуляторы» стоимости/кредита/страховки, ToDo-листы, CRM-системы.

    Конечно, все это можно сделать и без MVVM/Angular. Просто переход к MVVM мне видится как этап эволюции в разработке. Проводя аналогии с Desktop, в WPF подход MVVM используется повсеместно, хотя программирование в «стиле» WinForms с обращением к контролам типа textBox1.Text = «123» тоже вполне возможно. MVVM выиграл за счет своего удобства и легкой возможности тестирования.

    Основная прелесть IML в том, что позволяет уйти без использования каких то дополнительных инструментов.

    Основной вопрос — зачем уходить? Использование удобных оберток для сабмита формы на сервер и подмены блока html — ok, это безусловно полезно. Такие обертки есть у многих в виде библиотек, для этого нет необходимости переключаться на «фреймворк». Собственно, я и сам писал такое в свое время :)

    dsl.Utilities.Window.Alert(«Hello»)) — а вот полезность подобных конструкций у меня уже вызывает сомнения: «больше букв», меньше гибкости (в сравнении с plain js), требует изучения.

    Я собственно не к тому, что вы что-то делаете не так, или у вас «плохой фреймворк». При разработке Корпоративных информационных систем я уверен, что он дает большой выигрыш. Просто в этом посте изначально обсуждался React, и он используется скорее не в КИС, а в «модно/стильно/молодежных» приложениях. В этой сфере мне не кажется, что IML и Angular вообще конкурируют, у них разные ниши.

  4. в WPF подход MVVM используется повсеместно

    в WPF можно держать Connection открытым, если это «толстый клиент», а Web все таки больше адаптирован на submit form.

    dsl.Utilities.Window.Alert(«Hello»)) — а вот полезность подобных конструкций у меня уже вызывает сомнения: «больше букв», меньше гибкости (в сравнении с plain js), требует изучения.

    Интеграция типизации на всех этапах ? Вы вырезали код из контекста всего примера и конечно по этому JS вариант будет короче, но dsl.Utilites используется в 3 из 100 сценариях и это не главная особенность IML, посмотрите например blog.incframework.com/ru/power-selector/.

    Такие обертки есть у многих в виде библиотек, для этого нет необходимости переключаться на «фреймворк»

    Вы строите мнение о IML, только по примерам кода из одной статьи, посмотрите code , live demo . Browsio это проект, который демонстрирует большую часть возможностей Incoding Framework.

    и он используется скорее не в КИС, а в «модно/стильно/молодежных» приложениях.

    Не думаю, что задачи от этого меняются, мы делали доски объявлений и системы обслуживания медицинских учреждений, но в целом те же самые JSON to HTML , потом куча манипуляций с DOM и т.д.

    Я понимаю, что для изучения IML требуется время, но разве есть инструменты, которые не требуют изучения ? Я вижу IML, как аналог Nhibernate или Entity Framework, которые абстрагируют от SQL ( мы от JS ).

  5. Любой хотя бы мельком видевший Ember человек знает, что это полная противоположенность «низкоуровневости и многословности».

  6. Пиздец, долой AngularJS и Knockout
    Ангуляр нужен для SPA, а Нокаут нужен для Датабайдинга:)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *