html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

AngularJS vs. KnockoutJS

Добрый день уважаемые, хабрачеловеки.
В данной статье я хочу поделиться с вами своим опытом работы с такими фреймворками как AngularJS и Knockout.
Cтатья будет интересна тем, кто хорошо знаком с JavaScript-ом и имеет представление хотя бы об одном из упомянутых фреймворков и естественно желает расширить свой кругозор.

Обзор


AngularJS и Knockout очень близки по своей идеологии. Они являются фреймворками для динамических веб-приложений и используют HTML в качестве шаблона. Они позволяют расширить синтаксис HTML для того, чтобы описать компоненты вашего приложения более ясно и лаконично. Из коробки они устраняют необходимость писать код, который раньше создавался для реализации связи model-view-controller.AngularJS и Knockout — это по сути то, чем HTML и JavaScript были бы, если бы они разрабатывались для создания современных веб-приложений. HTML — это прекрасный декларативный язык для статических документов. Но, к сожалению, в нем нет многого, что необходимо для создания современных веб-приложений.

Features


  • Data-binding: простой и хороший способ связи UI и модели данных.
  • Мощный набор инструментов для разработчика (в частности у AngularJS, Knockout имеет достаточно бедный набор)
  • Легко расширяемый инструментарий


Архитектура приложения


Согласно документации, Angular предлагает структурировать приложение, разделяя его на модули. Каждый модуль состоит из:
  • функции, конфигурирующей модуль — она запускается сразу после загрузки модуля;
  • контроллера;
  • сервисов;
  • директив.
Контроллер в понимании Angular — это функция, которая конструирует модель данных. Для создания модели используется сервис $scope, но о нем немного дальше. Директивы — это расширения для HTML.
В свою очередь, Knockout предлагает строить приложение, разделяя его на ModelView, которые являются миксом из модели и контроллера. В пределах объекта ko.bindingHandlers размещены data-bindings, которые являются аналогами директив Angular. Для построения связи между моделью и ее представлением используются observable и observableArray.
Говоря о модульности, нельзя не вспомнить про шаблон AMD — Asynchronous Module Definition. Angular и Knockout не имеют собственной реализации AMD шаблона. Советую использовать библиотеку RequireJS. Она себя очень хорошо зарекомендовала в плане совместимости и с Angular, и с Knockout. Больше интереcной информации о ней вы найдете тут: http://www.kendoui.com/blogs/teamblog/posts/13-05-08/requirejs-fundamentals.aspx и http://habrahabr.ru/post/152833/.

Шаблонизация





(Отдельная благодарность разработчикам AngularJS за такую прекрасную картинку)

На данный момент уже существует огромное количество шаблонизаторов. К примеру, jQuery Templates (к сожалению, уже не поддерживается). Большинство из них работают по принципу: возьми статический template как string, смешай его с данными, создав новую строку, и полученную строку вставь в необходимый DOM-элемент посредством innerHTML свойства. Такой подход означает ререндеринг темплейта каждый раз после какого-либо изменения данных. В данном подходе существует ряд известных проблем, к примеру: чтение вводимых пользователем данных и соединение их с моделью, потеря пользовательских данных из-за их перезаписи, управление всем процессом обновления данных и/или представления. Кроме того, данный подход, на мой взгляд негативно сказывается на производительности.
Angular и Knockout используют иной подход. А именно two-way binding. Отличительная особенность данного подхода — это создание двунаправленной связи элемента страницы с элементами модели. Такой подход позволяет получить достаточно стабильный DOM. В Knockout двунаправлення связь реализована посредством функций observable и observableArray. Для анализа шаблона используется HTML парсер jQuery (если подключен, в противном случае аналогичный родной парсер). Результатом работы упомянутых функций является функция, которая инкапсулирует текущее состояние элемента модели и отвечает за two-way binding. Данная реализация, на мой взгляд, не очень удобна поскольку возникает проблема связанная с копированием состояния модели: скоуп функции не копируется, поэтому необходимо сперва получить данные из элемента модели обратившись к нему, как к функции и только после этого клонировать результат.
В Angular двунаправленная связь строится непосредственно компилятором (сервис $compile). Разработчику нет необходимости использовать функции подобные observable. На мой взгляд, это намного удобнее поскольку нет необходимости использовать дополнительные конструкции и не возникает проблемы при копировании состояния элемента модели.
Ключевой же разницей в реализации шаблонизаторов в Angular и Knockout является способ рендеринга элементов: Angular генерирует DOM-элементы, которые потом использует; Knockout — генерирует строки и innerHTML-ит их. Поэтому генерация большого числа элементов занимает у Knockout больше времени (наглядный пример немного ниже).

Модель данных


Говоря о модели данных в Angular, обязательно стоит остановится на сервисе $scope. По сути это и есть модель данных. Поскольку Angular предполагает наличие достаточно сложной архитектуры приложения, $scope также имеет более сложную структуру.
Внутри каждого модуля создается новый экземпляр $scope, который является наследником $rootScope. Существует возможность програмно создать новый экземпляр $scope из существующего. В таком случае созданный экземпляр будет наследником того $scope, из которого он был создан. Разобратся с иерархией $scope в Angular не составит труда для тех, кто хорошо знает JavaScript. Такая возможность очень удобна, когда есть необходимость создания различных widgets, к примеру pop-ups.

Data-binding


Binding в Knockout, directive в Angular используются для расширения синтаксиса HTML, то есть для обучения браузера новым трюкам. Детально разбирать концепцию data-bindings и directives я не буду. Хочу лишь отметить, что data-binding это единственный в Knockout способ отображения данных и их связи с представлением.
Более подробно данный вопрос рассмотрен в статьях:
AngularJS: http://habrahabr.ru/post/164493/, http://habrahabr.ru/post/179755/, http://habrahabr.ru/post/180365/
KnockoutJS: http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html
Отдельно хочется упомянуть про наличие фильтров у Angular. Фильтры используются для форматирования выводимых на экран данных. К сожалению, Knockout для всего использует bindings.


Примеры


Fade-in анимация
AngularJS: http://jsfiddle.net/yVEqU/
var ocUtils = angular.module("ocUtils", []);
ocUtils.directive('ocFadeIn', [function () {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            $(element).fadeIn("slow");
        }
    };
}]);

function MyCtrl($scope) {
    this.$scope = $scope;
    $scope.items = [];
    $scope.add = function () {
        $scope.items.push('new one');
    }
    $scope.pop = function () {
        $scope.items.pop();
    }
}

Knockout: http://jsfiddle.net/fH3TY/
var MyViewModel = {
    items: ko.observableArray([]),
    fadeIn: function (element) {
        console.log(element);
        $(element[1]).fadeIn();
    },
    add: function () {
        this.items.push("fade me in aoutomatically");
    },
    pop: function () {
        this.items.pop();
    }
};
ko.applyBindings(MyViewModel, $("#knockout")['0']);

Думаю, что проще этого примера будет сложно что-то найти, он отлично демонстрирует синтаксис фреймворков.

Fade-out анимация
AngularJS: http://jsfiddle.net/SGvej/
var FADE_OUT_TIMEOUT = 500;
var ocUtils = angular.module("ocUtils", []);
ocUtils.directive('ocFadeOut', [function () {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            scope.$watch(attrs["ocFadeOut"],
            function (value) {
                if (value) {
                     $(element).fadeOut(FADE_OUT_TIMEOUT);
                }
            });
        }
    };
}]);

function MyCtrl($scope, $timeout) {
    this.$scope = $scope;
    $scope.items = [];
    $scope.add = function () {
        $scope.items.push({removed: false});
    }
    $scope.pop = function () {
        $scope.items[$scope.items.length - 1].removed = true;
        $timeout(function () {
            $scope.items.pop();
            console.log($scope.items.length);
        }, FADE_OUT_TIMEOUT);
    }
}

Knockout: http://jsfiddle.net/Bzb7f/1/
var MyViewModel = {
    items: ko.observableArray([]),
    fadeOut: function (element) {
        console.log(element);
        if (element.nodeType === 3) {
            return;
        }
        $(element).fadeOut(function () {
            $(this).remove();
        });
    },
    add: function () {
        this.items.push("fade me in aoutomatically");
    },
    pop: function () {
        this.items.pop();
    }
};
ko.applyBindings(MyViewModel, $("#knockout")['0']);

Данный пример не намного сложнее, чем предыдущий, но есть несколько нюансов.
В случае с Angular, fadeOut должен быть выполнен до удаления элемента, поскольку DOM-елемнт связан с этим элементом модели и будет удален в тот же миг, когда будет удален элемент. Также важно отметить, что удаление элемента модели из массива стоит выполнять через сервис $timeout. Этот сервис по сути является оберткой для функции setTimeout и гарантирует целостность модели данных.
У Knockout возникает проблема другого характера. Функция fadeOut получает в качестве первого аргумента массив DOM-элементов, относящихся к данному элементу модели. Иногда при странном стечении обстоятельств в процессе рендеринга шаблона могут быть созданы и соответственно они будут присутствовать в получаемом массиве, поэтому необходимо делать проверку элементов прежде чем выполнять fadeOut. Также по окончанию процесса fadeOut не забывайте удалять DOM-элементы (они не удаляются автоматически).

Popup
AngularJS: http://jsfiddle.net/vmuha/EvvY7/, http://angular-ui.github.io/bootstrap/ (по второй ссылке вы найдете достаточно много хороших и полезных решений)
var ocUtils = angular.module("ocUtils", []);

function MyCtrl($scope, $compile) {
    var me = this;
    this.$scope = $scope;
    $scope.open = function (data) {
        var popupScope = $scope.$new();
        popupScope.data = data;
        me.popup = $("<div class=\"popup\">{{data}}<br /><a href=\"#\" ng-click=\"close($event)\"> Close me</a></div>");
        $compile(me.popup)(popupScope);
        $("body").append(me.popup);
    }
    $scope.close = function () {
        if (me.popup) {
            me.popup.fadeOut(function () {
                $(this).remove();
            });
        }
    }
}

Knockout: http://jsfiddle.net/vmuha/uwezZ/, http://jsfiddle.net/vmuha/HbVPp/
var jQueryWidget = function(element, valueAccessor, name, constructor) {
    var options = ko.utils.unwrapObservable(valueAccessor());
    var $element = $(element);
    setTimeout(function() { constructor($element, options) }, 0);
    //$element.data(name, $widget);
};

ko.bindingHandlers.dialog = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
            console.log("init");
            jQueryWidget(element, valueAccessor, 'dialog', function($element, options) {
                console.log("Creating dialog on "  + $element);
                return $element.dialog(options);
            });
        }        
};
    
ko.bindingHandlers.dialogcmd = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {          
            $(element).button().click(function() {
                var options = ko.utils.unwrapObservable(valueAccessor());
                $('#' + options.id).dialog(options.cmd || 'open');
            });
        }        
};

var viewModel = {
    label: ko.observable('dialog test')
};

ko.applyBindings(viewModel);

Реализовать popup можно по разному. Через директиву или байндинг и как часть ViewModel или модуля.
В Angular для popup необходимо будет создавать новый экземпляр $scope, об этом я уже упоминал выше, и использовать сервис $compile для компиляции шаблона.
В Knockout также скорей всего понадобится создание новой ModelView и вызова функции applyBindings для связи модели и представления.Думаю стоит заметить, что в случае, если для popup будет создана новая модель данных, то в Knockout возникнет проблема получения доступа к $rootModel из шаблона popup. Иерархия модели данных в Knockout построена на DOM-элементах, соответственно, если контейнер popup находится за пределами контейнера для приложения, то popup не будет иметь доступ к $rootModel.

Форматирование цены
AngularJS: http://jsfiddle.net/vmuha/k6ztB/1/
Knockout: http://jsfiddle.net/vmuha/6yqDw/


Производительность


Перейдем к вопросу производительности. Были произведены 2 теста: холодный старт приложения “Hello World!” и рендеринг массива из 1000 элементов.
На всех схемах по вертикали — миллисекунды, по горизонтали номер эксперимента.




Здесь хорошо видно, что холодный старт у Knockout происходит на много быстрее, чем у Angular.




А вот, когда речь заходит о рендеринге, здесь очевидно лидирует Angular. Как мы видим для рендеринга 1000 строк Knockout тратит до 2,5 секунд в то же время Angular хватает меньше 500 миллисекунд для выполнения этой задачи. Кроме того, отображение отрендеренных элементов на экране пользователя также занимает разное время: для Angular это 1-3 секунды, а для Knockout — 14-20 секунд. Это происходит из-за того что Knockout генерирует строки, а Angular — DOM-элементы.


Резюме


Самый главный вопрос для меня заключался в определении области применения Angular и Knockout. Проведя несколько простых экспериментов, я сделал следующие выводы:
Knockout применим в случаях, когда нет необходимости в создании сложной архитектуры, сложных workflow-ов. Его основная функция — связь модели и представления, поэтому его лучше всего использовать для простых одностраничных приложений. К примеру, создание различного уровня сложности форм.
Относительно Angular я пришел к выводу, что он будет полезен в тех случаях, когда требуется создание RichUI. Настоящего и полноценного one-page приложения со сложной архитектурой и сложными связями.


P.S.:

Надеюсь, данная статья будет всем интересна. Буду рад прочитать ваши комментарии, отзывы и конструктивную критику! Желаю всем приятной работы!
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

1

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции