AngularJS и ReactJS или как сделать ваше приложение быстрее

    Современный мир программирования, а особенно веб и javascript уже давно не тот и имеет очень большое количество инструментов для той или иной задачи. В сегодняшнем посте я хотел бы рассказать как скрестить мощь AngularJS и молниеносность отображения view – Facebook React.

    Всем известно, что когда мы генерируем коллекцию во view через Angular, то каждый элемент этой коллекции становится observable. Я конечно понимаю и знаю что есть определенный набор библиотек и решений как это обойти, но сегодня речь не об этом. Что же дает нам React? Ну одно из его преимуществ, это jsx синтаксис, который есть не что иным как html в javascript. Также есть возможность создавать reusable компоненты, наследовать их и использовать всю мощь что позволяет делать эта библиотека. Что же приступим.

    Первым делом создадим простой проект с банальным названием ng-with-react. Для установки библиотек я воспользуюсь bower. С помощью него установим AngularJS, ReactJS и Twitter bootstrap дабы наше приложение имело хоть какой-то презентабельный вид. Создадим index.html и main.js которые будут иметь код:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>AngularJS with Facebook react</title>
    
        <link rel="stylesheet" href="app/vendor/bootstrap/dist/css/bootstrap.css"/>
    
        <style>
            .user-list .user-item {
                border-left: none;
                border-right: none;
            }
            .user-list .user-item:last-child {
                border-bottom: none;
            }
        </style>
    
        <script src="app/vendor/angular/angular.js"></script>
        <script src="app/vendor/react/react.js"></script>
    
        <script src="app/main.js"></script>
    
    </head>
    <body ng-app="app">
        <div class="container" ng-controller="MainCtrl as main">
    
            <div class="page-header">
                <h1>A experiment with AngularJS and ReactJS</h1>
            </div>
    
            <div class="panel panel-default">
                <div class="panel-heading">Just AngularJS</div>
                <ul class="list-group">
                    <li class="list-group-item" ng-repeat="user in users" ng-click="main.getUser(user)">
                        {{ user.firstName }} - {{ user.lastName }}
                    </li>
                </ul>
            </div>
        </div>
    </body>
    </html>
    

    И

    (function(ng, React) 
        var app = ng.module('app', []);
    
        var UserFactory = function() {
            var users = [
                {id: 1, firstName: "Denis", lastName: "Stoyanov", age:26},
                {id: 2, firstName: "Alex", lastName: "Alexeev", age:24},
                {id: 3, firstName: "Peter", lastName: "Petrov", age:21},
                {id: 4, firstName: "Ivan", lastName: "Ivanov", age:20}
            ];
    
            return {
                users: users
            };
        };
    
        var UserCtrl = function($scope, $log, User) {
            $scope.users = User.users;
    
            this.getUser = function(user) {
                $log.info('A selected user %O', user);
            };
        };
        app.factory('User', [UserFactory]);
    
        app.controller('MainCtrl', ['$scope', '$log', 'User', UserCtrl]);
    
    
        return app;
    
    })(angular, React);
    

    Соответственно.

    Что у нас есть в арсенале сейчас. Мы имеем простейшее приложение на ng в котором есть простейший контроллер и сервис, который отдает фейковые данные. На стороне представления мы имеем простейший список, который рендерит наши данные. Также у нас есть обработчик по клику на элемент, который записывает юзера в консоль. Все довольно просто и легко.
    Теперь давайте создадим новую директиву для отображения этих же данных, но только через ReactJS. Как подобает в лучших домах Парижа, нам стоит использовать директивы для манипулирования DOM. Для начала создадим React class для рендеринга списка. Его код будет выглядет примерно так:

    var UserList = React.createClass({
            displayName: "UserList",
            render: function() {
                var users = this.props.scope.users;
                var scope = this.props.scope;
    
                var userList = users.map(function(user, index, array) {
                    var clickHandler = scope.$apply.bind(scope,
                        scope.userSelected.bind(null, {user: user}));
    
                    return React.DOM.li({ className: "user-item list-group-item", onClick: clickHandler },
                        [user.firstName, user.lastName].join(' - '));
                });
    
                return React.DOM.ul( {className:"user-list list-group"}, userList);
            }
        });
    

    Как можно заметить в свойствах React есть ряд переменных, которых нам надо будет передать путем простой передачи scope в React class. Также создадим директиву, которая будет использовать этот класс как представление.

    var UserDirective = function() {
            return {
                restrict: 'AE',
                scope: {
                    users: '=',
                    userSelected: '&'
                },
                link: function(scope, element, attrs) {
                    scope.$watchCollection('users', function() {
                        React.renderComponent(UserList({scope: scope}), element[0]);
                    });
                }
            };
        };
    
        app.directive('userList', UserDirective);
    

    В ней видно, что будем мы ее использовать как элемент или же аттрибут, в нее будут переданы данные с юзерами и функция для вывода юзера в консоль. Чтобы наше представление отображалось при изменении коллекции и создадим простейший watcher и на его изменения будет перерисовывать наше представление. И наконец мы срендерим наш React class в тот елемент, на который был повешен аттрибут user-list. В index.html после рендеринга на Angular добавим такой код:

            <div class="panel panel-default">
                <div class="panel-heading">AngularJS with ReactJS</div>
                <div user-list users="users" user-selected="main.getUser(user)"></div>
            </div>
    

    Вот такими простыми манипуляциями мы связали Angular и React. Приложение простейшее, но оно дает понять концепт и в дальнейшем можно усложнять. По моим тестам при 2000 елементов view на React было в 5 раз быстрее чем на Angular.
    На скриншоте мы можем видеть, что у view на React нет байндингов в отличии от AngularJS.



    Также мы можем посмотреть, что нам сгенерировал React.



    Надеюсь мой эксперимент будет кому-то полезен. Я считаю, что надо компоновать решения по их возможностям и если писать на ReactJS представления удобней и в отображении они быстрее, то я буду делать так.
    Если есть какие-то нюансы, welcome в комментарии =)

    Код примера Source
    Share post

    Comments 14

      +4
      Если нужны исходники, могу выложить на гитхаб =)
        +1
        Ссылки на иструменты для профилирования Angular и React
          0
          пардон, инструменты =)
        +4
        А зачем вообще в таком случае ангуляр? Обработчики можно и с реактом повесить, и кода не сильно больше.
        Кроме того, вы смотрели на библиотечку OM для React? Она правда на clojurescript и тянет за собой новую кухню, но код получается очень лаконичный.
          0
          Затем, что ng полноценный фреймворк и очень легко на нем писать, просто немного не оптимизирован под большое колличество данных. Я же не говорю делайте так! Плюс, меня дико удивляют эти попытки юзать clojurescript и прочее. Зачем столько всего устанавливать, для того чтобы писать простой фронтенд на js?
            +1
            А чем react неполноценен? Вроде те же задачи решает.
              +2
              А зачем использовать AngularJS или ReactJS для простого фронтенда? Т.е. их можно для него использовать, но сравнивать, как мне кажется, надо для другого кейса. Они — как и ClojureScript — хороши как раз для того, чтобы не сойти с ума при сложном фронтенде.
            +1
            Маленькое замечание. Поменяйте последовательность листингов местами после фразы
            Создадим main.js и index.html которые будут иметь код:
            ...
            Соответственно
            , либо порядок перечисления имен файлов, т.к. этот порядок не соответствует их следованию
              +5
              Конгениально было бы замерить собственно выигрыш в мс или Кб. Коль скоро в заголовке указано быстрее, то хотелось бы раскрытия темы на сколько.
                +5
                Ускорение в 5 раз — неплохо, но полученный код не так легко сопровождать.
                Мне больше импонирует как сделали ребята из Scalyr. Они просто избавились от лишних watcher-ов, стали использовать DOM cache, что дало довольно большой профит 1200ms to 35ms.
                Вот их репозиторий на Github.
                  +4
                  Хорошая идея. Можно еще использовать bindonce, но уж точно не тащить целый здоровенный react ради этого
                  –2
                  del
                    +7
                    По моим тестам при 2000 елементов view на React было в 5 раз быстрее чем на Angular.
                    «Раз пошла такая пьянка», решил сравнить с vanillaJS, на 20k элементов:
                    React: ~700ms
                    JS: ~160ms
                    JS (без onclick): ~80ms
                    Код примерно такой.
                      0
                      Круто, Денчик. Мне нравиться, такое решение. Хотел начать разбираться с React'ом, но все никак руки не доходили. Теперь есть повод. Спасибо)

                      Only users with full accounts can post comments. Log in, please.