ANGULARJS + REQUIREJS

Original author: Dmitry Evseev
  • Translation
  • Tutorial

Во время разработки проектов мы полюбили AngularJs. Но также мы встретили некоторые трудности в борьбе за чистоту модульности, с которой AngularJs справляется хорошо, но все же порой ему чего-то не хватает. RequireJs оказывается полезным там, где AngularJs оставляет желать лучшего, но использование их вместе — не совсем тривиальная задача.

Итак, опишем наш взгляд на решение проблемы.

Для чего?

Работая с AngularJs вы обязательно задумаетесь о правильной организации кода. Конечно уже существуют отличные примеры решения проблемы. Например можно изучить теоретический пост Брайана Форда или практическое руководство Клиффа Мейерса. Я же поделюсь способом организации кода в приложениях AngularJs с использованием RequireJs.

Данный подход будет полезен, если вы хотите:

  • перестать беспокоиться о подключении скриптов в правильном порядке;
  • загружать javascript код асинхронно;
  • иметь возможность скомпилировать код в один минифицированный JS-файл;

Для кого?

Предполагается, что вы уже знаете основы AngularJs, а так же по крайней мере знакомы с подходом AMD и библиотекой RequireJs. Чтобы проиллюстрировать подход, я сначала подключу RequireJs для Angular Seed и опишу вносимые изменения. Angular Seed структурирует код, разделяя файлы по назначению. Таким же образом поступлю и я.

Как?

Angular Seed Project

Давайте познакомимся со структурой кода в Angular Seed. Взгляните на код примера на github:

  • app.js файл используется для инициализации загрузки и конфигурирования приложения.
  • сами файлы приложения — контроллеры (controllers), директивы (directives), фильтры (filters), сервисы (services).
  • index.html с подключенными JS-файлами в нужном порядке.
  • или index-async.html, который использует angular-loader.js или сторонние загрузчики, для загрузки зависимостей асинхронно.

Что ж, начнем.

Добавляем RequireJs

Взгляните на пример в браузере или на github.

Установка зависимостей

Для этого я использовал bower. Код bower.json:
 {
    "name": "AngularJS + RequireJS Example",
    "version": "0.1",
    "main": "index.html",
    "ignore": [
      "**/.*",
      "libs"
    ],
    "dependencies": {
     "angular": "latest",
     "requirejs": "latest",
     "requirejs-domready": "latest"
   }
 }


Положите файл .bowerrc рядом с bower.json и запустите bower install. Теперь все необходимые зависимости находятся в директории libs.

index.html

Разрушение будет хорошим началом! Откройте index.html и удалите все теги . Выглядит чище, не так ли? Теперь подключите один скрипт перед закрывающим тегом , который будет загружать библиотеку RequireJs, и поручит ей искать конфигурацию в файле js/main.js, указанном в атрибуте data-main тега
:

<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>My AngularJS AngularJS + RequireJS App</title> <link rel="stylesheet" href="css/app.css"> </head> <body> <ul class="menu"> <li><a href="#/view1">view1</a></li> <li><a href="#/view2">view2</a></li> </ul> <div data-ng-view></div> <div>Angular Require seed app: v<span app-version></span></div> <script src="lib/requirejs/require.js" data-main="js/main.js"></script> </body> </html>


Это все что нам необходимо. Закройте файл index.html, поскольку он нам больше не потребуется.

main.js

Приступим к конфигуации RequireJS.

require.config({
 
     //  псевдонимы и пути используемых библиотек и плагинов
     paths: {
         'domReady': '../lib/requirejs-domready/domReady',
         'angular': '../lib/angular/angular'
     },
 
     // angular не поддерживает AMD из коробки, поэтому экспортируем перменную angular в глобальную область
     shim: {
         'angular': {
             exports: 'angular'
         }
     },
 
     // запустить приложение
     deps: ['./bootstrap']
});


Что только что произошло?

В блоке paths мы устанавливаем псевдонимы используемых библиотек и плагинов. Далее указываем что AngularJs должен быть виден в глобальной области видимости (для этого используется блок shim). Так же указыавем, что для запуска приложения должен быть загружен bootstrap.js.

bootstrap.js

Сейчас нам нужно настроить AngularJs, именно для этого используется файл bootstrap.js (обратите внимание, что вам больше не нужно использовать ng-app в HTML коде приложения). Первым делом нам потребуется routes.js, который содержит конфигурацию маршрутов приложения, поэтому добавим его в список зависимостей.

Заметьте, что в этом модуле мы почти не имеем дело с асинхронной загрузкой, и цепочка подключения скриптов всегда будет
angular->app->routes так как они зависят от друг друга: AngularJs необходим до создания модуля приложения (app) который уже должен существовать при конфигурировании маршрутов (routes).

 /**
  * bootstraps angular onto the window.document node
  */
 define([
     'require',
     'angular',
     'app',
     'routes'
 ], function (require, ng) {
     'use strict';
 
     require(['domReady!'], function (document) {
         ng.bootstrap(document, ['app']);
     });
});


Мы используем модуль domReady, чтобы пред запуском приложения убедиться, что DOM загружен. Также обратите внимание, что для запуска необходимо загрузить зависимость app.js, которая является основой приложения.

app.js

app.js содержит определение модуля верхнего уровня и загружает зависимости его подмодулей.

define([
     'angular',
     './controllers/index',
     './directives/index',
     './filters/index',
     './services/index'
 ], function (ng) {
     'use strict';
 
     return ng.module('app', [
         'app.services',
         'app.controllers',
         'app.filters',
         'app.directives'
     ]);
});


Мы условились использовать четыре основных модуля: контроллеры (controllers), директивы (directives), фильтры (filters), сервисы (services). Указываем, что эти модули должны быть загружены до определения основного модуля.

routes.js

Конфигурация маршрутов (routes) осуществляется в этом файле. Кроме того возможно указывать маршруты для каждого модуля в отдельности (этот случай выходит за рамки данной статьи).

define(['./app'], function (app) {
     'use strict';
     return app.config(['$routeProvider', function ($routeProvider) {
         $routeProvider.when('/view1', {
             templateUrl: 'partials/partial1.html',
             controller: 'MyCtrl1'
         });
 
         $routeProvider.when('/view2', {
             templateUrl: 'partials/partial2.html',
             controller: 'MyCtrl2'
         });
 
         $routeProvider.otherwise({
             redirectTo: '/view1'
         });
     }]);
});


Структура модуля

Модули состоят из трех частей:

  • объявление модуля
  • компонент
  • загрузчик

Давайте рассмотрим модуль app.controllers для примера.

Объявление модуля controllers/module.js

define(['angular'], function (ng) {
    'use strict';
    return ng.module('app.controllers', []);
});


Этот файл будет использоваться компонентами модуля для назначения самих себя (см. следующую часть)

Загрузчик модуля controllers/index.js

Это просто пустой блок define, который включает в себя все компоненты модуля. Здесь не нужно упоминать файл module.js так как он будет запрошен самими компонентами (в данном случае контроллерами). Загрузчик подключается как зависимость верхнего уровня модуля приложения. Благодаря ему RequireJs знает какие файлы загружать.

define([
    './my-ctrl-1',
    './my-ctrl-2'
], function () {});


Компонент модуля controllers/my-ctrl-1.js

В случае с модулем контроллера app.controllers компонентами будут отдельные контроллеры приложения. Пример определения контроллера:

define(['./module'], function (controllers) {
    'use strict';
    controllers.controller('MyCtrl1', [function ($scope) {}]);
});


Обратите внимание, что мы использовали ссылку на ./module.js для того чтобы получить модуль и присоединить компонент к модулю.

Выводы:

Вот и все. Теперь мы имеем рабочее AngularJs приложение основанное на RequireJs. Вы можете наслаждаться разработкой не обращая внимания на порядок подключения ваших скриптов, а также этот подход позволит вам с легкостью минифицировать и объединить все файлы приложения в один.
Share post

Comments 66

    +5
    Angular Seed структурирует код, разделяя файлы по назначению.

    Хотя такой подход может быть неплохим начальным пособием для наглядности, никогда не делайте так в реальных проектах средних и больших размеров. Больше информации по теме в блоге AngularJS:

    docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub
      –1
      Пользуюсь данным подходом (с некоторыми доработками) и не могу сказать что он имеет какие-либо существенные недостатки. Конечно стоит добавить, что независимые модули приложения удобнее разбивать на несколько таких структур как в примере.
      например
      app/
      	contollers/
      	directives/
      	...
      admin/
      	...
      forum/
      	...
      



      Так же если у вас множество контроллеров, то удобнее распределить контроллеры по директориям, указав в загрузчике модуля (index.js) соответствующие пути:
      например
      controllers/
      	user/
      		login.js
      		profile.js
      	catalog
      		...
      	contacts
      		...
      


        0
        Чтобы судить о недостатках, нужно попробовать разные подходы. Вы сначала попробуйте структуру, которую официально советуют разработчики AngularJS по приведенной мной ссылке выше, и тогда уже можно будет сравнивать.
          0
          Я пробовал различные подходы, и это мне показался наиболее удобным.
          Что касается структуры, которую вы привели в пример — я ее обязательно изучу, хотя на первый взгляд она мне кажется менее удобной и структурированной (простите за каламбур!).
            +5
            Смысл в том, что в рекурсивной многоуровневой модульной структуре отдельным юнитом считается не программная сущность, а кусок функциональности проекта. Все html и css/less/sass (и иногда даже картинки и прочие файлы) хранятся вместе с соответствующими js и их тестами, чего нет смысла делать, когда у вас в структуру проекта введены чисто программные понятия вроде controllers и services. Это невероятно повышает читаемость проекта и эффективность разработки, не надо постоянно лазить по дереву файлов, большинство юнитов получаются хорошо инкапсулированными. Попробуйте, вам понравится.
              –1
              Судя по всему вы больше работаете на front end. Не всегда есть возможность получать html шаблоны разбросанные по различным директориям, в которых к тому же содержатся «css/less/sass (и иногда даже картинки и прочие файлы)», так как зачастую шаблоны — это не статические файлы. Их генерацией занимается серверная часть приложения, которая может расставить имена полей формы в соответствии с моделью или перевести статические тексты на язык, полученный из GET параметра.
              Таким образом вам придется настраивать сложные маршруты, которые будут перенаправлять такие запросы на соотв. контроллер на сервере, или же получать их централизованно от специально созданного контроллера (/templates/<templatename>.html?lang=en), что на мой взгляд удобнее.

              Что качается такой статики как изображения и файлы стилей, то на мои взгляд держать из вместе с js скриптами не удобно, хотя это наверно дело привычки.

              Может быть подскажете, есть ли заготовка приложения со структурой описанной вами? Было бы интересно попробовать.
                0
                Ну, в идеале, если это отдельное приложение — оно вообще целиком статика, работающее с Web-API (http/websockets). И его проще воспринимать именно как отдельное приложение, а не как некую структуру, части которой генерируются на сервере. То есть, после загрузки страницы, все данные идут в виде JSON.

                Стили я тоже не сторонник хранить разбросанными по приложению. Они вообще лежат за пределами папки приложения — у них просто сильно другая структура организации и очень много абстрактных/общих компонентов.
                  –1
                  Я только что привел пример части приложения, которое не может быть статичным. Не удобно хранить различные шаблоны для каждого языка приложения и править их все при малейшем изменении, поэтому они генерируются на сервере. То же самое касается изображений. Они могут отдаваться сервером в различном размере в зависимости, например, от размера экрана клиента, хотя и доступны по одному адресу. Да и стили могут быть различны.
                  Мне думается что приложение которое «целиком статика» — в некотором роде миф.
                    0
                    Да отнюдь не миф — более того, еще и легко упаковывается в Cordova/PhoneGap и запускается на мобильниках (обычно спец-версия с дополнительными оптимизациями).

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

                    Для какого «каждого языка»? Шаблоны Angular-приложения — это шаблоны, которые доступны только этому приложению, это его view-слой.

                    Они могут отдаваться сервером в различном размере в зависимости, например, от размера экрана клиента, хотя и доступны по одному адресу.

                    Зачем? А media-queries? А если прямо отдельное что-то, например, для особых платформ, вроде старых мобильников — то проще делать отдельное приложение для них, нежели утяжелять/усложнять основное, поскольку самый камень преткновения, обычно, скрипты, а не разметка.
                      +2
                      Мне думается что приложение которое «целиком статика» — в некотором роде миф.

                      en.wikipedia.org/wiki/Single-page_application

                      Забыл упомянуть, что речь идет, естественно, о SPA-приложениях — то есть о том, для чего в первую очередь создавался AngularJS и на что он заточен. Просто последние 2 года я пишу только их и поэтому уже подзабыл, что нынче кто-то еще делает иначе.
                      0
                      любопытно. расскажите про ваш способ организации исходников приложения и стилей подробнее.
                        +2
                        А тут все очень просто.

                        Папки:
                        static/
                          css/     (здесь хранится скомпилированный, но еще не уменьшенный css, содержит source-maps)
                          fonts/   (здесь подключаемые шрифты)
                          img/     (здесь общие картинки и SVG, которые не меняются, лого, иконки)
                          js/
                            app/ (здесь Angular приложение)
                              ...
                              components/ (повторно используемые, независимые от бизнес-логики компоненты)
                              core/       (здесь основа приложения, layout-директивы, лэйауты, аутентификация, и так далее)
                                controllers/
                                models/
                                services/
                                templates/      (использование отдельных подпапок "templates" - дело вкуса и размера проекта)
                                  header.html
                                  main-content.html
                                  ...
                              features/         (различные элементы, "фичи", специфичные для приложения)
                                account/
                                  controllers/
                                  services/
                                  models/
                                  templates/
                                  init.js       (подключаемый модуль "фичи", собирает все контроллеры, сервисы, роуты)
                                  routes.js     (роуты "фичи")
                                some-feature1/
                                some-feature2/
                              ...
                              app.js      (сам файл приложения, подключение зависимостей, и прочее)
                              init.js     (загрузочный файл приложения, аналог bootstrap.js из топика)
                              routes.js   (общие маршруты приложения и маршруты по-умолчанию)
                            main.js   (файл настроек require.js, обычно он же запускает по-умолчанию модуль "app/init", директория "static/js" устанавливается базовой для require.js)
                          lib/   (здесь находятся все зависимости для клиента, bower устанавливает свои компоненты сюда)
                          sass/
                            ...
                            _some-styles.scss (файлы с префиксами - партиалы, подключаются в app.css
                            app.scss (файл стилей, только он компилируется sass-ом)
                        index.html (точка входа, загружаемая страница может генерироваться сервером, а может отдаваться в статическом виде напрямую из корня проекта)
                        


                        Пара замечаний:
                        — Во время запуска build-процесса в корне создается папка dist. Ее структура повторяет папку проекта в целом, но содержит оптимизированные версии js/css-файлов.
                        — Для production все шаблоны собираются в отдельный js-файл, являющийся AMD-модулем, содержимое которого включается в основной JS-файл приложения (то есть, шаблоны не загружаются асинхронно в продакшене).
                          0
                          А что вы думаете об этой структуре предложенной Google?
                          https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub
                            0
                            Весьма неплохо! По предложенной мной структуре выше, кстати — она постоянно развивается, улучшается и упрощается — что очень хорошо.

                            Например, ввиду использования Angular UI Router, я перестал создавать отдельные папки для контроллеров, и стал помещать контроллеры прямо в папку конкретной «feature», или в отдельные файлы наподобие some-list-state.js — где описывается как контроллер, так и другие элементы «состояния» (в терминах ui-router), всякие resolve, onEnter, и некоторые кастомные расширения (например, breadcrumbs).
                      0
                      artch частично прав, так как html/css/js это только слой ui компонент приложения, а любое приложение должно быть разделено на слои. Самые распространённые это: хэлперы, модели, бизнес логика, слой базы данных, сервисы, ui. Мы к дополнению делим любое приложение на проекты, это перекочевало у нас ещё с .net: есть solution и projects. Каждый проект это можно сказать одно законченное приложение, которое может соответственно зависеть от других проектов. Для примера могу привести недавно начатый полу-хобби проект: task scheduler. Хоть это и nodejs приложение, но структура идентична и для single-page. И вот то, что относится к ui, мы делим на компоненты/контролы и прочее, и вот там (видите как мы уже глубоко зашли), каждый отдельный юнит держится со всеми своими зависимостями и ресурсами. Как например ajax-loader. Огромным плюсом является, то что с таким подходом компоненты легко разрабатывать( это можно делать иногда вне контекста всего проекта), и переносить в другие приложения. А вот для релиза, когда собирается приложение, весь код и ресурсы которые вне проекта складываются в папку build.
                      Конечно же подобная структура зависит от технологий, а как мы знаем Web — это просто джунгли из языков, фреймворков и разных типов приложений, так что серебренной пули здесь нет.
                0
                А теперь представьте, что вам потребуется выпилить один модуль. Например, модуль user, т.к. в другом вашем приложении не будет регистрации и т.п.
                  0
                  А в чем собственно проблема? Если модуль независим, и в остальных модулях нет с ним множества связей, то решение задачи будет не сложнее чем в подходе предложенном artch.

                  Я согласен, что для больших предложений и в определенных условиях лучше использовать такую структуру, но как я уже говорил, хранение файлов изображений, стилей, шаблонов и скриптов в одной директории для меня несколько засоряет ее, к тому же это не всегда возможно. Например, когда изображения (или стили, шаблоны) отдаются пользователям из хранилища (например из базы) с предварительной обработкой.

                    0
                    tamtakoe наверное имеет в виду, что вам придется выдирать этот модуль из дерева несколько раз, так как у вас несколько точек входа в один и тот же модуль (controllers, services, directives, и т.д.). Это повышает риск чего-то где-то забыть или сделать не так, по сравнению с тем, как если бы точка входа была одна, а структура делилась бы по функциональности, а не по типу.

                    А еще в рекурсивной структуре удобно делать вот такие штуки:
                    Скрытый текст
                    students/  
                        students.js
                        students-list.js  
                        students-list.spec.js  
                        students-view/
                            students-view.js
                            students-view.spec.js
                            students-edit-dlg/
                                students-edit-dlg.js
                                students-edit-dlg.spec.js
                            students-sidebar/
                                students-sidebar.js
                                students-sidebar.spec.js
                        students-filtering/
                            students-filtering.js
                            students-filtering.spec.js
                    



                    В вашей же структуре это все будет на одном плоском уровне. Про себя скажу, что я где-то года полтора назад ушел с подобного вашему подхода, когда понял, что он не дает масштабировать большое приложение, и очень рад, что ребята из AngularJS наконец-то сами подтвердили, что структура Angular Seed неэффективна для больших SPA-приложений.
                      0
                      … удобно делать вот такие штуки

                      Выше я показывал как делать такие штуки при подходе, описанном в переводе. Никто не запрещает делать многоуровневую структуру из плоской. Другое дело что в этих подходах модулем считаются различные структуры.
                        +1
                        Не соглашусь, что ваш пример демонстрирует эти же штуки. Одно дело — просто распределить файлы по подкаталогам, и совсем другое — моделировать с помощью дерева файлов иерархию связей между компонентами. Ваш пример — это такая же одноуровневая плоская структура, просто с подкаталогами внутри.
                0
                Так же если у вас множество контроллеров, то удобнее распределить контроллеры по директориям, указав в загрузчике модуля (index.js) соответствующие пути:

                А как вы поступите если вам для некоторой сборки нужно будет отключить определенный модуль? Для больших проектов жизненно необходимо разбивать именно по модулям.
              +8
              перестать беспокоиться о подключении скриптов в правильном порядке;

              Об этом можно и так не беспокоиться, если вынести создание всех модулей в один файл, скажем bootstrap.js, и подключать его первым.
              загружать javascript код асинхронно;

              А зачем? в продакшен сборке вы все равно будете использовать сконкатеннированную и заминифицированную версию.
              иметь возможность скомпилировать код в один минифицированный JS-файл;

              и Grunt и Gulp это легко делают.

              Зато из минусов у Вас теперь:
              1. Необходомость заворочивать в define создание любой сущности ангуляра, которая и так создается в модуле ангуляра.
              2. Дополнительный файл конфигурации RequireJS.
                0
                А зачем? в продакшен сборке вы все равно будете использовать сконкатеннированную и заминифицированную версию.

                Если это труЪ SPA и он большой, то не каждый пользователь будет ждать когда загрузится один большой файл, особенно если ему из него нужно только 30 строчек, которые в отдельном файле намного быстрее загрузятся.
                  0
                  А require.js все равно не обеспечивает «ленивой загрузки».
                    0
                    Разве JavaScript, как и любая другая статика не кэшируется браузером?
                      0
                      Зависит от.
                      0
                      И что я не так сказал?)
                        +1
                        А require.js все равно не обеспечивает «ленивой загрузки».

                        Именно это он и обеспечивает при необходимости. Ну это так, к слову :)
                          0
                          Покажите, как.
                            0
                            Есть свои хакерские способы, и даже работают, но я бы в большинстве случаев сам бы не советовал так усложнять.

                            Другой способ — когда мы изначально подключаем все компоненты Angular, но в каждом из них только по необходимости запускаем загрузку модуля, который непосредственно исполняет код. То есть, к примеру, в качестве контроллера создаем функцию, которая сначала все загружает, и только по факту загрузки выполняет нужную работу. Таким образом, приложение изначально «знает» обо всех своих компонентах, но не в курсе, что они на самом деле делают. Не самый гибкий способ, но также вполне работоспособный. Только надо аккуратно запускать $digest-цикл вручную.
                              0
                              Обе ссылки имеют довольно слабое отношение к require.js.
                              То, что уже в загруженном приложении можно лениво инициализировать angular-сущности (или любые другие) — понятно и ежам.

                              Речь-то шла как раз о ленивой подгрузке именно AMD-модулей, т.е. отдельных файлов. Популярный миф, что в require.js это есть.
                                0
                                Популярный миф, что в require.js это есть.

                                Когда люди говорят с такой уверенностью, кажется что они говорят незыблемую истину. В require.js это есть.
                                  +1
                                  То же самое вам скажу: покажите. С радостью признаю свою неправоту.
                                    0
                                    Если в ответ на какое либо событие подгрузить файл (или набор файлов) модуля при помощи функции require() это не является ленивой загрузкой?
                                      0
                                        0
                                        Если в одном из контроллеров будет вызов require():
                                        $scope.getSomeModule = function(){
                                            require(['lazyLoaded.js'], function(lazyLoaded){
                                              ...
                                            });
                                        }
                                        

                                        Файл lazyLoaded.js загрузится только после выполнения метода $scope.getSomeModule() (например в ответ на клик или другое событие). Если же метод не выполнится, файл так и останется не загруженным. Поправьте, если я не прав, но это как раз и есть отложенная загрузка.
                                          0
                                          Попробуйте и посмотрите в инспекторе во вкладке Network, когда загрузится lazyLoaded.js.
                                            0
                                            Попробуйте и посмотрите в инспекторе во вкладке Network, когда

                                            Пример
                                              0
                                              Ну, я рад, что оказался не прав. Спасибо.
                                  0
                                  AMD-модули асинхронны по-определению. Никто не мешает вызвать «require» внутри какой-нибудь функции, и загрузка модуля начнется только после этого (если не был ранее затребован и загружен каким-нибудь другим вызовом «require»).

                                  Пример подхода можно посмотреть здесь. Особое внимание нужно уделить предотвращению повторных загрузок и параллельных загрузок одного и того же модуля.
                                    0
                                    При таком способе requirejs парсит то, что ему передано в качестве модуля регулярками (поэтому всякая условная и динамическая загрузка тоже не работает), находит вызовы require и подгружает все — а потом, при вызове, возвращает.

                                    Асинхронна в AMD только подгрузка модулей, а не их определение. Люди поумнее меня пишут свои модульные системы, чтобы обойти это ограничение.
                                      0
                                      А в чем проблема? Lazy-loading то можно реализовать. Я понимаю, как работает require, если ему передать строку — require('some-module') — он будет искать этот модуль, причем еще до загрузки основного модуля, который содержит такой вызов (парсит все содержимое функции в виде строки). А что насчет require(['module-name'], function (module) {… })? То, что возвращает загруженные модули — это понятно, более того, поддерживает их состояние.

                                      Насчет собственных модульных систем — ни к чему, как по мне. Сам не особо рад оберткам в виду «define» для всех AMD-модулей (отчасти решается build-процессом), но тут еще вопрос совместимости с другими разработками и сторонними библиотеками. Ну и сейчас уже, похоже, время переходить на ES6-модули, где, наконец-то, определение модуля не зависит от того, как он будет загружаться.
                                        0
                                        Насчет ES6-модулей согласен.
                                          0
                                          К слову, сейчас транс-компиляция все равно идет в один из существующих форматов модулей в зависимости от того, каким образом нужно модули использовать. И если нужно что-то лениво подгружать — в итоге технически используются именно AMD-модули для этого.
                                        0
                                        Асинхронна в AMD только подгрузка модулей, а не их определение.

                                        не совсем так. в AMD определение модулей асинхронно, что следует прямо из названия. именно эта концептуальная особенность и позволяет проделывать всякие технические трюки, включая асинхронную подгрузку кода.

                                        в YM же надстраивали возможность для асинхронного инстанцирования модулей. по смыслу это аналогично тому, что делает плагин requirejs-promise.
                                  0
                                  Мы используем github.com/marcoslin/angularAMD в большом проекте. Ни разу не было проблем. Работает как с ngRouter, так и с uiRouter
                                  +2
                                  В контексте angular — не обеспечивает, так как для запуска приложения нужно что бы оно было загружено все и целиком, к моменту когда будет запущен этап конфигурации. То есть описанный в вашей статье метод этой проблемы не решает, а без него толку особо и нету.
                                    0
                                    Простите, статья не ваша.
                              +1
                              Если это настолько большой SPA, то я бы сделал прогрес бар при загрузке приложения, куда лучше чем дополнительный запрос на сервер при каждом изменении его состояния.
                                0
                                http2 грядет ;)
                                  0
                                  http2 это конечно хорошо, но особо разницы в производительности не будет (если сравнивать собранную версию и набор файликов). Вот Angular2 с ленивой подгрузкой модулей грядет, это да.
                              0
                              про это, кстати, был веселый доклад на ng-conf 2014 www.youtube.com/watch?v=4yulGISBF8w и эпичный тред stackoverflow.com/questions/12529083/does-it-make-sense-to-use-require-js-with-angular-js — с аргументами за то, когда и зачем такое может быть нужно. и про r.js/almond тоже. если вкратце:

                              1. сокрытие внутренней файловой структуры компонентов.
                              2. подключение внешних библиотек.
                              3. поддержание общего порядка.

                              где-то тут же — загрузка тестов, сборка разных бандлов и прочее подобное счастье. понятно, что в сыром виде, как описывалось в этой статье, использование параллельно двух различных систем контроля различных зависимостей превращается в полное непотребство. но оригинал был написан осенью 2013 (ага, я сходил по ссылке). но в начале этого года tuchk4@ предлагал здесь уже гораздо более интересное решение: habrahabr.ru/post/216469/. любопытно, есть-ли какие-то новости про использование плагина в продакшене?
                              0
                              Скажите, а что заставляет художников рисовать такие тени, как на картинке?
                                0
                                Звонили из нулевых, просили отдать такие тени обратно.
                                  0
                                  … художников рисовать такие тени,...

                                  Вы так говорите, как будто я орудовал кистью по холсту, и вышло у меня, мягко говоря, хреново.
                                  Хотя последнего я не исключаю…

                                  Это мое проявление нонконформизма по отношению к современному плоскому дизайну.
                                  0
                                  RequereJS — классный
                                    +4
                                    Пришел к выводу, что использование RequireJS в связке с Angular — не самый удобный вариант. Двойное описание dependencies в define и затем практически то же самое в модуле создает огромное количество лишнего кода. Использую конкатенацию в gulp/grunt для небольших проектов без dependency hell и browserify для проектов посложнее.
                                      0
                                      Для джанги есть отличный django-compressor, который позволяет писать так

                                              {% compress js %}
                                                  <script src="{{ STATIC_URL }}bower_components/angular/angular.js"></script>
                                                  <script src='{{ STATIC_URL }}bower_components/angular-ui-router/release/angular-ui-router.js'></script>
                                                  <script src="{{ STATIC_URL }}bower_components/angular-resource/angular-resource.js"></script>
                                                  <script src="{{ STATIC_URL }}bower_components/angular-sanitize/angular-sanitize.js"></script>
                                                  <script src='{{ STATIC_URL }}bower_components/underscore/underscore.js'></script>
                                                  <script src='{{ STATIC_URL }}bower_components/angular-bootstrap/ui-bootstrap.min.js'></script>
                                                  <script src='{{ STATIC_URL }}bower_components/angular-bootstrap/ui-bootstrap-tpls.js'></script>
                                                  <script src='{{ STATIC_URL }}bower_components/angular-gettext/dist/angular-gettext.min.js'></script>
                                              {% endcompress %}
                                      

                                      На деве — все грузится как обычно. На проде — автоматом выдает собранную версию.
                                      0
                                      Dmitry Evseev, а как в вашем примере связать все с angular-route?
                                        0
                                        Dmitry Evseev — это автор статьи, которая является переводом!
                                        … а как в вашем примере связать все с angular-route?

                                        Опишите, пожалуйста, подробнее в чем проблема? Вы хотите запрашивать JS фалы в зависимости от маршрута или не знаете как подключить файл модуля (angular-route.js)?
                                          0
                                          Извинитие, протупил с автором ))

                                          Ну вот в примере с angular-seed используется angular-route, а в статье это не упоминается. Я попробовал по аналогии — не получилось.

                                          Я добавлял angular-route в файле app.js как зависимость requirejs и как зависимость при создании модуля angular.
                                            0
                                            Обычно я делаю следующим образом:
                                            app.js:
                                            define(
                                                [
                                                    'angular',
                                                    './angular-modules',
                                                    './controllers/index',
                                                    './directives/index',
                                                    './filters/index',
                                                    './services/index'
                                                ], 
                                                function (ng) {
                                                    
                                                    return ng.module('app', [
                                                        'app.services',
                                                        'app.controllers',
                                                        'app.filters',
                                                        'app.directives',
                                                        'ngRoute',
                                                        //... другие модули
                                                    ]); 
                                                }
                                            );
                                            


                                            Обратите внимание, что в зависимости добавлен файл angular-modules.js

                                            angular-modules.js:
                                            define(
                                                [
                                                    'angular',
                                                    './../ngmodules/angular-route.min', 
                                                    //... другие модули
                                                ], 
                                                function () {}
                                            );
                                            
                                              0
                                              Спасибо за ответ, буду пробовать.

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