В этой статье я хочу предложить архитектурное решение: разбиение приложения на несколько несвязанных с точки зрения фреймворка частей и самостоятельная реализация связей между ними.
В Angular существует понятие bootstrap. Это метод, который обычно вызывается после загрузки страницы, если существует элемент с атрибутом ng-app. Он связывает этот элемент c указанным в значении атрибута модулем. Такой элемент должен быть один, иначе документация ничего не гарантирует. Однако можно использовать его вручную: angular.bootstrap(element, [/*Module*/]);
При этом будет запущен указанный модуль, все его зависимости, а также модуль ng и его зависимости. Поэтому у нового приложения (назовем его изолированным модулем) будет свой $injector, $rootScope, $compile и т.д. — вся внутрення кухня Angular будет создана заново. Родительский изолированный модуль не будет знать о существовании вложенных в него, между модулями не будут проходить события (emit и broadcast), а $digest, вызванный в одном изолированном модуле, не будет просачиваться в другой. Для слабо связанных компонент это то, что надо.
Для удобного создания новых приложений может быть использована следующая директива:
directive('newApp', function () { return{ restrict: 'EA', transclude: true, scope: { module: '=' }, link: function (scope, element, attr, ctrl, transclude) { var div = document.createElement('app'); var module = angular.module(scope.$id, [scope.module]).run(['$rootScope', function ($rootScope) { scope.$on('$destroy', function() { $rootScope.$destroy(); angular.module(scope.$id, []); }); transclude($rootScope, function (el) { angular.element(div).append(el); element.append(div); }); }]); angular.bootstrap(div, [scope.$id]); } }; });
Использование:
<body ng-app="App"> <new-app module=" 'SomeModule' "> <some-module-directive/> </new-app> </body>
Для сравнения производительности до и после можно посмотреть синтетический пример: медленный вариант и быстрый вариант.
Отдельно стоит упомянуть про утечки памяти. За их отсутствие отвечает обработчик события $destroy в директиве. Он отправляет это событие внутрь изолированного модуля, чтобы все об этом узнали и перезаписывает модуль, чтобы удалить зарегистрированные директивы, контроллеры и др. Однако, память все-таки утекает, например из-за кэша элементов в angular.element.cache и много чего другого. Этот вопрос заслуживает отдельного исследования и статьи.
Еще одна обнаруженная проблема — сервис $location. Помимо прочего, он наблюдает за адресом страницы, и при его изменении делает какие-то телодвижения, например обновляет содержимое ngView. В случае нескольких изолированных модулей будет создано несколько экземпляров $location, несколько обработчиков изменения url, что не есть хорошо. Пока что придумал следующий обходной путь:
.config(function ($locationProvider) { $locationProvider.$get = function () { return angular.element(document).injector().get('$location'); }; })
Сейчас провожу тестирование и оформление кода в виде библиотеки, интересует мнение сообщества.
ссылка на оригинал статьи http://habrahabr.ru/post/213705/
Добавить комментарий