Статьи

Использование RequireJS в приложениях AngularJS

При написании больших приложений на JavaScript одной из самых простых вещей, которую можно сделать, является разделение базы кода на несколько файлов. Это повышает удобство сопровождения кода, но увеличивает вероятность пропуска или неправильного размещения тега script в вашем основном HTML-документе. Отслеживание зависимостей становится затруднительным по мере увеличения количества файлов. Эта проблема сохраняется и в больших приложениях AngularJS. У нас есть ряд инструментов, которые заботятся о загрузке зависимостей в приложении.

В этой статье мы увидим, как использовать RequireJS с AngularJS, чтобы упростить работу по загрузке зависимостей. Мы также рассмотрим, как использовать Grunt для создания комбинированных файлов, содержащих модули RequireJS.

Краткое введение в RequireJS

RequireJS — это библиотека JavaScript, которая помогает лениво загружать зависимости JavaScript. Модули — это просто файлы JavaScript с некоторым синтаксическим сахаром RequireJS. RequireJS реализует асинхронные модули, определенные CommonJS. RequireJS предлагает простые API для создания и ссылки на модули.

RequireJS нужен главный файл, который содержит основные данные конфигурации, такие как пути к модулям и прокладки. Следующий фрагмент main.js показывает скелет файла main.js :

 require.config({ map:{ // Maps }, paths:{ // Aliases and paths of modules }, shim:{ // Modules and their dependent modules } }); 

Все модули в приложении не нужно указывать в разделе путей. Другие могут быть загружены, используя их относительные пути. Чтобы определить модуль, нам нужно использовать блок define() .

 define([ // Dependencies ], function( // Dependency objects ){ function myModule() { // Can use the dependency objects received above } return myModule; }); 

Модуль может иметь несколько зависимых модулей. В общем случае объект возвращается в конце модуля, но это не обязательно.

Внедрение зависимостей Angular против RequireJS Dependency Management

Один из распространенных вопросов, которые я слышу от разработчиков Angular, касается различия между управлением зависимостями Angular и RequireJS. Важно помнить, что назначение обеих библиотек совершенно разное. Система внедрения зависимостей, встроенная в AngularJS, работает с объектами, необходимыми в компоненте; в то время как управление зависимостями в RequireJS работает с модулями или файлами JavaScript.

Когда RequireJS пытается загрузить модуль, он проверяет все зависимые модули и загружает их в первую очередь. Объекты загруженных модулей кэшируются и обслуживаются при повторном запросе тех же модулей. С другой стороны, AngularJS поддерживает инжектор со списком имен и соответствующих объектов. Запись добавляется в инжектор, когда создается компонент, и объект обслуживается всякий раз, когда на него ссылаются, используя зарегистрированное имя.

Совместное использование RequireJS и AngularJS

Загружаемый код, включенный в эту статью, представляет собой простое приложение, содержащее две страницы. Он имеет следующие внешние зависимости:

  • RequireJS
  • JQuery
  • AngularJS
  • Угловой маршрут
  • Угловой Ресурс
  • Угловой интерфейс ngGrid

Эти файлы должны быть загружены непосредственно на страницу в том порядке, в котором они перечислены здесь. У нас есть пять пользовательских файлов сценариев, содержащих код необходимых компонентов AngularJS. Давайте посмотрим, как эти файлы определены.

Определение компонентов AngularJS как модулей RequireJS

Любой компонент AngularJS состоит из:

  • Определение функции
  • Внедрение зависимости
  • Регистрация на угловой модуль

Из вышеперечисленных трех задач мы будем выполнять первые две задачи внутри отдельных модулей, в то время как третья задача будет выполняться в отдельном модуле, который отвечает за создание модуля 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. Перед закрытием модуля мы возвращаем функцию конфигурации, чтобы ее можно было отправить в зависимый модуль для дальнейшего использования.

Мы используем тот же подход для определения любого другого типа компонента Angular, так как в этих файлах нет кода, специфичного для компонента. Следующий фрагмент демонстрирует определение контроллера:

 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; }); 

Угловой модуль для приложения зависит от каждого из модулей, определенных до этого момента. Этот файл получает объекты из всех других файлов и подключает их с помощью модуля AngularJS. Этот файл может возвращать или не возвращать что-либо в результате этого файла, на Angular модуль можно ссылаться откуда угодно с помощью angular.module() . Следующий блок кода определяет угловой модуль:

 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 Для этого необходимо, чтобы файл, определяющий угловой модуль, был загружен первым. Код для этого файла показан ниже.

 require(['app/ideasModule'], function() { angular.bootstrap(document, ['ideasApp']); } ); 

Настройка Grunt для объединения модулей RequireJS

При развертывании тяжелого приложения JavaScript файлы сценариев должны быть объединены и уменьшены для оптимизации скорости загрузки файлов сценариев. Такие инструменты, как Grunt, пригодятся для автоматизации этих задач. Он имеет ряд задач, определенных для облегчения любого процесса развертывания интерфейса. У него есть задача grunt-contrib-requirejs для объединения файловых модулей RequireJS в правильном порядке и последующей минимизации полученного файла. Как и любая другая основная задача, ее можно настроить так, чтобы она велась по-разному для каждого этапа развертывания. В демонстрационном приложении можно использовать следующую конфигурацию:

 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.