AngularJS — разделение приложения на модули и загрузка компонентов с помощью RequireJS

в 8:11, , рубрики: AngularJS, javascript, requirejs, метки: , ,

Использование AngularJS в паре с RequireJS — достаточно популярный подход к разработке веб приложений в последнее время. И один из основных вопросов — структура приложения. Существует достаточно известный seed для такого приложения tnajdek/angular-requirejs-seed, но мне это не походит, так как при увеличении функционала приложения — данная структура просто будет засоряться кучей файлов, не будет никакого логического разделения скриптов и достаточно сложно будет их менеджить.

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

Модуль

В данном случае, это логически отдельная часть приложения, включающая в себя набор компонентов:

  • ngModule;
  • Controller;
  • FIlter;
  • Directive;
  • Service;
  • Template;
  • Configs — содержат config() и run() методы для текущего ngModule.

AngularJS — разделение приложения на модули и загрузка компонентов с помощью RequireJS

Проблема

При использовании RequrieJS, файлы приложения чаще всего подключаются как-то так:

require('modules/foo/controller/foo-controller.js');
require('modules/foo/service/foo-service.js');
require('modules/foo/directive/foo-controller.js');
require('text!modules/foo/templates/foo.html');
require('modules/bar/directive/bar-controller.js');

Здесь есть явные минусы:

  • Код очень зависит от структуры проекта;
  • Код очень зависит от названий модулей;
  • Достаточно много нужно писать руками.

Решение

Были написаны RequireJS плагины для загрузки компонентов модуля.

К примру есть такая структура приложения (кстати, очень похожая на структуру бандлов в Symfony2):

app
   |-modules
   |       |-menu
   |       |    |-controller
   |       |    |          |-menu-controller.js
   |       |    |-menu.js    
   |       |    
   |       |-user
   |             |-controllers
   |             |           |-profile.js
   |             |-resources
   |             |           |-configs
   |             |           |        |-main.js
   |             |           |
   |             |           |-templates
   |             |           |          |-user-profile.html
   |             |           |-directives
   |             |                      |-user-menu 
   |             |                                 |-user-menu.js
   |             |                                 |-user-menu.html
   |             |-src
   |             |      |-providers
   |             |      |          |-profile-information.js 
   |             |      |-factory
   |             |              |-guest.js
   |             |-user.js
   |
   |-application.js
   |-boot.js

В данном случаем у нас есть 2 модуля: user и menu. Файлы /app/modules/menu/menu.js и /app/modules/user/user.js — скрипты с инициализацией angularJS модулей. Все остальное — думаю понятно.

Теперь нужно задать конфигурацию для подключения всех компонентов. Делается это с помощью requirejs.config:

requirejs.config({
  baseUrl: '/application',
  paths: {
    'text': '../bower_components/requirejs-text/text',

    // Structure plugins:   
    'base': '../bower_components/requirejs-angualr-loader/src/base',
    'template': '../bower_components/requirejs-angualr-loader/src/template',
    'controller': '../bower_components/requirejs-angualr-loader/src/controller',
    'service': '../bower_components/requirejs-angualr-loader/src/service',
    'module': '../bower_components/requirejs-angualr-loader/src/module',
    'config': '../bower_components/requirejs-angualr-loader/src/config',
    'directive': '../bower_components/requirejs-angualr-loader/src/directive',
    'filter': '../bower_components/requirejs-angualr-loader/src/filter'
  },
  structure: {
      prefix: 'modules/{module}',

      module: {
        path: '/{module}'
      },      
      template: {
        path: '/resources/views/{template}',
      },

      controller: {
        path: '/controllers/{controller}'
      },

      service: {
        path: '/src/{service}'
      },

      config: {
        path: '/resources/configs/{config}'
      },

      directive: {
        path: '/resources/directives/{directive}/{directive}'
      },

      filter: {
        path: '/resources/filters/{filter}'
      }
    }
});

Все пути каждого компонента определены в рамках модуля. Поле structure.prefix — путь к корню модуля, после baseUrl.

Теперь, если мы хотим подключить файл /app/modules/user/user.js из:
1. /app.js:

require('module!user')

2. /app/modules/user/controllers/profile.js:

require('module!@')

В рамках одного модуля — имя модуля можно не писать, достаточно символа '@'. Тем самым, если придется переименовать модуль — не нужно будет менять код.

Теперь, если мы хотим подключить файл /app/modules/user/controllers/profile.js из:
1. /app.js:

require('controller!user:profile')

До двоеточия — название модуля, после двоеточия — название контроллера.

2. /app/modules/user/user.js:

require('controller!profile')

В рамках одного модуля — имя модуля можно не писать, достаточно указать только название контроллера. Так же, если контроллер лежит на уровень ниже, то возможно подключать так:

require('controller!additional/path/to/profile')

Точно так же и для всех других компонентов.

Результат

Получилось очень гибкая структура приложения с поддержкой разделения кода на модули и с минимальной зависимостью кода от структуры проекта даже если придется перенести какой либо компонент из одного модуля в другой — то практически ничего менять не придется. И лишнего кода так же стало меньше.

Так же не возникает никаких проблем при сброке проекта. В тестовом приложение есть пример собранного проекта в папке /build и Gruntfile для сборки, но в нем нету ничего не обычного.

Ссылки:

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

Автор: tuchk4

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js