- PVSM.RU - https://www.pvsm.ru -
При написании больших JavaScript-приложений одна из самых простых вещей, которую можно сделать, это разделить код на несколько файлов. Это улучшает поддерживаемость кода, но увеличивает шансы потерять или ошибиться со вставкой тега script в главный HTML-документ. Отслеживание зависимостей затрудняется с ростом числа файлов проекта. Эта проблема присутствует в больших AngularJS приложениях до сих пор. У нас есть целый ряд инструментов, которые заботятся о загрузке зависимостей в приложении.
В этой статье мы рассмотрим, использование RequireJS с AngularJS для упрощения загрузки зависимостей. Мы также рассмотрим, как использовать Grunt для генерации файлов, содержащих модули RequireJS.
RequireJS [1] это JavaScript-библиотека, которая помогает в «ленивой загрузке» JavaScript-зависимостей. Модули являются обычными JavaScript-файлами с некоторым количеством «синтаксического сахара» RequireJS. RequireJS реализует Asynchronous Module Definition, специфицированное в CommonJS. RequireJS предлагает простой API для создания модулей и обращения к ним.
Для RequireJS требуется главный файл, содержащий базовые конфигурационные данные, такие как пути к модулям и «прокладкам». Следующий фрагмент показывает каркас файла main.js:
require.config({
map:{
// Maps
},
paths:{
// Алиасы и пути модулей
},
shim:{
// Модули и их зависимости
}
});
Нет нужды задавать все модули приложения в секции paths. Они могут быть загружены с использованием относительных путей. Для объявления модуля мы должны использовать блок define().
define([
// Зависимости
], function(
// Объекты зависимостей
){
function myModule() {
// Может использовать объекты зависимостей, полученные выше
}
return myModule;
});
Модуль может не иметь каких-либо зависимостей. Обычно в конце модуля возвращается объект, но это не обязательно.
Один из распространенных вопросов, который я слышу от AngularJS-разработчиков, касается разницы в управлении зависимостями в AngularJS и RequireJS. Здесь важно вспомнить, что назначение обеих библиотек совершенно разное. Встроенная в AngularJS система внедрения зависимостей (dependency injection) работает с объектами, необходимыми в компонентах; в то время как управление зависимостями (dependency management) в RequireJS имеет дело с модулями или JavaScript-файлами.
Когда RequireJS пытается загрузить модуль, он сначала проверяет все зависимости и загружает их. Объекты загруженных модулей кешируются и предоставляются, когда те же модули запрашиваются снова. С другой стороны, AngularJS поддерживает инжектор со списком имет и соответствующих им объектов. Объект добавляется в инжектор, когда компонент создается и будет предоставлен, когда на него сошлются через зарегистрированное имя.
Код, включенный в статью, который можно загрузить здесь [2], является простым приложением, содержащим 2 страницы. Он имеет следующие внешние зависимости:
Эти файлы должны быть загружены непосредственно на страницу в приведенном здесь порядке. Еще мы имеем пять собственных файлов, содержащих код необходимых компонентов AngularJS. Давайте посмотрим как эти файлы определяются.
Любой компонент AngularJS состоит из:
Из этих трех задач, мы будем выполнять первые две внутри отдельных модулей (RequireJS), в то время как третья задача будет выполнена в виде отдельного модуля, который отвечает за создание модуля AngularJS.
Во-первых, давайте определим блок конфигурации. Блок конфигурации не зависит ни от каких других блоков и в конце возвращает функцию config. Но, прежде чем загрузить модуль config внутри другого модуля, мы должны загрузить все, что необходимо для блока конфигурации. Следующий код содержится в файле config.js:
define([],function(){
function config($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'templates/home.html',
controller: 'ideasHomeController'
})
.when('/details/:id',{
templateUrl:'templates/ideaDetails.html', controller:'ideaDetailsController'})
.otherwise({redirectTo: '/home'});
}
config.$inject=['$routeProvider'];
return config;
});
Обратите внимание на способ внедрения зависимостей, использованный в этом фрагменте. Я использовал $inject, чтобы получить внедренные зависимости, так как функция config, объявленная выше, является простой JavaScript-функцией. Перед закрытием модуля мы возвращаем функцию config, так что она может быть передана в зависимый модуль для дальнейшего использования.
Мы следуем этому подходу для определения любого другого типа компонентов AngularJS, кроме того мы не имеем никакого специфичного для компонентов кода в этих файлах. Следующий фрагмент показывает определение контроллера:
define([], function() {
function ideasHomeController($scope, ideasDataSvc) {
$scope.ideaName = 'Todo List';
$scope.gridOptions = {
data: 'ideas',
columnDefs: [
{field: 'name', displayName: 'Name'},
{field: 'technologies', displayName: 'Technologies'},
{field: 'platform', displayName: 'Platforms'},
{field: 'status', displayName: 'Status'},
{field: 'devsNeeded', displayName: 'Vacancies'},
{field: 'id', displayName: 'View Details', cellTemplate: '<a ng-href="#/details/{{row.getProperty(col.field)}}">View Details</a>'}
],
enableColumnResize: true
};
ideasDataSvc.allIdeas().then(function(result){
$scope.ideas=result;
});
}
ideasHomeController.$inject=['$scope','ideasDataSvc'];
return ideasHomeController;
});
Модуль Angular для приложения зависит от каждого из модулей, определенных до этого момента. Этот файл получает объекты от всех других файлов и цепляет их к модулю AngularJS. Этот файл может возвращать или не возвращать что-либо как результат, на него можно ссылаться из любого места с помощью angular.module(). Следующий фрагмент определяет модуль Angular:
define(['app/config',
'app/ideasDataSvc',
'app/ideasHomeController',
'app/ideaDetailsController'],
function(config, ideasDataSvc, ideasHomeController, ideaDetailsController){
var app = angular.module('ideasApp', ['ngRoute','ngResource','ngGrid']);
app.config(config);
app.factory('ideasDataSvc',ideasDataSvc);
app.controller('ideasHomeController', ideasHomeController);
app.controller('ideaDetailsController',ideaDetailsController);
});
Это приложение Angular не может быть запущено с использованием директивы ng-app, так как необходимые скрипты загружаются асинхронно. Правильный подход здесь состоит в использовании ручного запуска. Это должно быть сделано в специальном файле, именуемом main.js. Здесь нужно, чтобы сначала был загружен файл с определением модуля Angular. Код для этого файла показан ниже.
require(['app/ideasModule'],
function() {
angular.bootstrap(document, ['ideasApp']);
}
);
При развертывании больших JavaScript-приложений файлы скриптов следует объединять и минифицировать для оптимизации скорости их загрузки. Инструменты, подобные Grunt, могут пригодиться для автоматизации этих задач. Он имеет целый ряд задач, определенных чтобы сделать любой процесс front-end развертывания легче. У него есть задача grunt-contrib-requirejs для объединения файлов модулей RequireJS в правильном порядке и последующей минификации результирующего файла. Подобно любой другой задаче Grunt, она может быть сконфигурирована, чтобы вести себя по-разному для каждой стадии развертывания. Следующая конфигурация может быть использована в нашем демо-приложении:
requirejs: {
options: {
paths: {
'appFiles': './app'
},
removeCombined: true,
out: './app/requirejs/appIdeas-combined.js',
optimize: 'none',
name: 'main'
},
dev:{
options:{
optimize:'none'
}
},
release:{
options:{
optimize:'uglify'
}
}
}
Эта конфигурация будет создавать несжатый файл, когда Grunt будет запущен с опцией dev, и минифицированный файл в случае запуска Grunt с опцией release.
Управление зависимостями становится сложным, когда размер приложения превышает определенной количество файлов. Библиотеки, подобные RequireJS, позволяют легче определять зависимости и не беспокоиться о порядке загрузки файлов. Управление зависимостями становится неотъемлемой частью приложений JavaScript. AngularJS 2.0 будет иметь встроенную поддержку для AMD.
UPDATE: Было бы интересно услышать в комментариях, какими менеджерами зависимостей вы пользуетесь и что считаете лучшим вариантом.
Автор: victorburre
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/74885
Ссылки в тексте:
[1] RequireJS: http://requirejs.org/
[2] загрузить здесь: https://github.com/jsprodotcom/source/blob/master/AngularRequireJsSample.zip
[3] Источник: http://habrahabr.ru/post/243565/
Нажмите здесь для печати.