Самые непристойные и отвратительные наши поступки, выходящие за всякие нормы морали и принципов, обычно начианаются со слов «А почему бы и нет?».
В данном посте я затрону вопросы проектирования приложения на AngularJS, философии, его архитектуры, сборки, пройдусь по разным полезным библиотекам и готовым архитектурным шаблонам. В довершение устрою небольшую Angular+Require.js+Grunt+Yeoman оргию, которую я назвал Angular-Super-Seed или чуть более скромно ASS-генератор.
Так что, если вы планируете писать приложение на Angular, то почему бы и нет?
Мотивация
- «Поступай с людьми так, как хотел бы, чтобы поступили с тобой». Вдруг кому-то пригодится
- Узнать что-нибудь новенькое как о себе, так и о разработке в комментариях.
- Попиарить свой генератор приложения ASS.
Из чего состоит Angular приложение?
При условии, что приложение на Angular не состоит из сервисов, контроллеров, директив и шаблонов?
Правильно, приложение на Angular состоит из модулей и только из модулей.
Ок, умник, из чего состоят модули?
А вот модули в свою очередь уже могут состоять из сервисов, контроллеров, директив, сервисов, провайдеров и шаблонов.
Сервисы
Сервисы это, пожалуй, самый простой компонент с точки зрения его примения. Для чего он нужен? В большинстве случаев, сервис используется в качестве Модели в теории MVC, то есть для работы с данными. К примеру, вы можете создать сервис пользователя $user для хранения данных в куках или локальном хранилище. Или сервис $api — обертку для библиотечного $http, который будет осуществлять взаимодействие с вашим серверным API. Но сервисом могут быть и процессы, которые выполняются фоном, и просто библиотеки объектов, функций, которые вы планируете использовать в разных местах кода.
...
.factory("$user",function($rootScope, $someLocalStorageService){
var user={
id:null,
name:null,
isAuthorized:false
}
if($someLocalStorageService.load("user"))angular.extend(user,$someLocalStorageService.load("user"));
$rootScope.$watchCollection(function(){
return user;
},function(user){
$someLocalStorageService.save("user",user);
});
return user;
});
В данном примере сервис представляет собой как модель, так и фоновый процесс, который следит за изменением объекта и тут же сохраняет в локальном хранилище.
Теперь пару слов о том, где с этими сервисами можно работать.
Контроллеры
В контроллерах. Абстрагируемся от способов вызова контроллеров и обратимся к сути. А суть их проста связывать работу сервисов. И только в этом, хотя чаще всего кажется, что контроллер служит для того, чтобы связывать отображение (/верстку/DOM/HTML) с данными. И в прнципе изначально так и было. Раньше вы могли в контроллере, используя this обратиться к данным, которые используются в шаблоне.
...
.controller("SomeCtrl",function(){
this.var="холли вар";
});
...
<p>Святой {{var}}</p>
Но лавочку быстро прикрыли, и теперь, чтобы обратиться к этим данным вам нужен специальный сервис $scope. Поэтому запишем: «контроллеры — это структурный компонент, который используется для осуществления взаимодействия между сервисами».
Будь я преподавателем, принимающим экзамен у студента, лучше бы не сказал. Но, если смотреть правде в глаза, то в 99,99% случаев контроллеры будут вами использоваться именно для того, чтобы связать ui с логикой обработки данных.
Директивы
А вот за то, как связывать, эти самые данные с версткой отвечают директивы. Про то, что работа с DOM в Angular должна производиться только в директивах сказано много слов, поэтому, если к вам обратятся с вопросом "-А можно ли вызвать jQuery в контроллере", отвечайте "-Да, конечно", а когда тот развернется и довольный начнет уходить, хладнокровно стреляйте в затылок.
В чем смак директив? В том, что вы можете очень просто привязывать логику и данные к элементам ui. В принципе, если вы поставите перед собой такую цель, то вы можете напрочь забыть о таком термине как селектор.
Что есть директива? Нечто привязанное к элементу DOM. Как привязано? Как тэг, как аттрибут тэга, как класс тэга.
И в этом, на мой взгляд, и состоит вся суть ангулы. Роутинг, дата-байндинг, каркас — все это уже было до ангулы в разных фреймворках. А вот такой компактности и простоты проектирования ui, я лично нигде не встречал.
Вопрос сборки
Зачем она нужна? Чтобы при загрузке страницы на продакшене у вас было не сотни подключений для загрузки файлов проекта, а, в идеале, один. Чтобы при разработке не было необходимости ручками прописывать добавленные файлы.
Долгое время в крупных проектах и командах он решался с помощью самописных инструментов, сейчас для этого есть плагины Grunt/Gulp. И тут есть несколько аспектов. Суть этих плагинов в том, что все ваши скрипты/стили сшиваются в один. Значит нужно где-то прописать правила/зависимости и т.д., как эти файлы сшить. В коде самих файлов делать это будет неудобно, значит, нужно создавать дополнительные package-файлы. Но это еще не все. Очевидно, что это сшивание должно происходить после редактирования проекта, когда вы хотите протестировать результат. И здесь два варианта либо обновлять ручками, вызывая соответствующую команду, либо сделать, так называемый, watcher — скрипт, который мониторит все ваши файлы и производит сборку. В принципе, вариант неплохой, но при работе над большим проектом, можно получить ожидаемые проблемы с производительностью.
Поэтому я лично являюсь сторонником Require.js. Почему? Ну во-первых зависимости прописываются в коде, во-вторых, при разработке браузер сам подгрузит все необходимые файлы и сшивка не нужна, ну и в-третьих, когда дело дойдет до продакшена, все ваши файлы будут сжаты в один и, если захотитет, минифицированы.
Поэтому вопрос сборки — вопрос, который каждый решает для себя сам, нужна она или нет, и какими инструментами пользоваться. Главное, что играет роль при решении данного вопроса — архитектура.
Про архитекутуру
Здесь я опять вспомню, что приложение состоит из модулей. Поэтому когда вы планируете архитектуру своего приложения, вам стоит прикинуть из скольких модулей оно будет состоять. Оно может состоять только из одного модуля или наоборот иметь для каждой страницы сайта отдельный модуль. От чего это зависит? Во-первых, от вашего желания дробить даже небольшое приложение на модули. Во-вторых, от размера приложения. Чтобы было понятнее, вот условие.
(boolean) Буду ли я разбивать проект на модули = (boolean) Я разбиваю любые проекты на модули || (boolean) Проект большой
Ну и не маловажным будет то, как вы собираетесь тестировать свое приложение. Если вы хотите гонять тесты отдельно для контроллеров, сервисов, директив, то понятно, что вам нужно будет вынести их в отдельные модули «App.controllers», «App.services», «App.directives» и т.д.
Но что касается архитектуры, всегда приятно смотреть на то, как делают другие.
NGBP
ЛГБТ, как раз, предполагает, что ваш проект будет состоять из множества модулей. В приложении есть главный модуль, в котором описывается роутинг и прочие начальные установки, а остальная логика распределяется по компонентам.
Что бросается в глаза? То, что все ваши контроллеры, сервисы, директивы и прочее должны описываться в одном файле модуля. И хоть я лично являюсь приверженцем простоты, но даже на мой взгляд это уже будет перебором. И это на мой взгляд главный минус данного шаблона.
Angular-seed
На мой взгляд, отлично подходит для небольших и средних приложений. Проект состоит хоть и формально из нескольких модулей, но это как раз тот случай, когда контроллеры, сервисы и директивы, собираются вместе, поэтому с архитектурной точки зрения можно считать, что модуль только один.
Плюсы очевидны — все просто, понятно и компактно. Минусы, тоже — если вы будете делать большой проект, в большой команде, вам будет тесно, вдобавок ко всему сборка и минификация приложения не предусмотрена.
Angular-require-seed
Если вам понравился angular-seed, но хочется использовать require, то, пожалуйста, используйте. Плюсы, минусы те же, что и у Angular-seed. Разве, что за счет использования require.js вам будет проще компилировать свой проект, но это, если вы используется require-оптимизатор
Angular-super-seed
Я же, размышляя на тему архитектуры приложения, на тему того, чего не хватает и что бы еще предложить сообществу, пришел к такому результату.
В чем особенность? Вы можете дробить свой проект на модули, а можете ограничиться только основным. В плане хранения контроллеров, сервисов и директив я пошел я еще дальше. Все они хранятся в отдельных файлах. Более того, для каждой директивы создается отдельный шаблон и отдельный файл стилей.
Модули компонент задаются и описываются так же в отдельном файле.
Очевидным минусом такого подхода является то, что при создании каждого нового контроллера/сервиса/директивы необходимо выполнять кучу рутиных операций для их объявления. То есть, вы хотите создать новую директиву: создайте файл директивы, файл шаблона, файл стилей. Не смертельно, но поднадоесть может. И вот здесь пришло время сказать YO ASS !
Yeoman
Когда я в первый раз зашел на сайт Yeoman фишки сервиса я не оценил. Ну можешь ты создавать отдельные файлы по определенным шаблонам. Ну так это я и в редакторе могу настроить.
Но вот когда я задумался о том, что хорошо бы не просто отдельные файлы создавать, но и при этом выполнять и еще каки-нибудь операции, вот тогда я-то и присмотрелся к товарищу старшине.
В резульате чего получился такой вот генератор.
Ставим Yeoman
npm install -g yo
Ставим генератор
npm install -g generator-ass
В чем кайф? В том что вы можете сделать у себя в консоли вот так:
yo ass
И у вас в папке появится новое приложение. Ну а дальше — больше.
yo ass:page main
Автоматически создаст новый шаблон для страницы, файл стилей, контроллер и подключит это все в роутер
Создадим модуль
yo ass:module first
Модуль готов и уже сдан под ключ.
И теперь создадим в нем директиву
yo ass:module firstDirective
отвечаем на вопрос, в каком модуле и все ок.
Сразу скажу, что не хватает пока тестирования и моков. Все будет.
На этом все, надеюсь, кому-нибудь пригодится. Почему бы и нет?
Автор: durovchpoknet