Все нормально с «расшифровкой» хорошо написанного полгода назад Angular-кода, пусть и нетривиального. Его главный недостаток, как мне видится — отсутствие какого-то единого направления по работе с моделями и, иногда, слишком много способов решить одну задачу.
Насчет атрибутов — это одна из киллер-фич, однозначно. Никакие они не магические. Директивы, которые «цепляются» к атрибутам — это самый что ни на есть «unobtrusive JavaScript». Например, вы же можете добавить какой-нибудь атрибут, вроде 'data-tooltip=«Hi I am a tooltip»' к элементу, чтобы потом с помощью JS найти этот элемент и реализовать логику этого самого tooltip-а. То же самое и здесь, только возможностей куда больше. По сути, ни для чего, кроме вашего приложения такой атрибут смысла не несет в любом случае, а директиву можно привязать к любому из существующих элементов или атрибутов.
Насчет изучения множества новых концепций — согласен, порог входа низок, но чтобы сделать что-то реальное — вначале нужно сломать немало копий.
Не спорю — веб-компоненты — шикарная штука, очень ждем ее и все такое. Однако, это никак не избавляет от проблем, связанных с самой концепцией SPA-приложений. Просто какие-то вещи решаются нативными средствами, но логику-то все равно писать на старом добром JS, который с поисковиками (пока) не дружит. Что касается чистоты, инкапсуляции и элегантности подхода в целом — все гуд.
Веб-Компоненты и React/Ember/Angular/etc — никак друг другу не противоречат.
React.js — это генерация разметки с помощью JS, а значит поисковые системы это не увидят, а значит нужно делать fallback на серверном языке, а значит двойная работа.
Во-первых, вопрос касается не только React — это относится к любым генерируемым на клиенте UI. А во-вторых — все современные поисковики поддерживают индексацию AJAX-контента (на примере Гугла, но и у Яндекса, и у Бинга тоже это есть). Кроме прочего, Гугл активно «учится» выполнять JS, ну и напоследок — задача индексации замечательно решается, просто нужен другой подход — сервисы, вроде Prerender.io именно для этих целей.
Ну в Backbone то тоже низкий, чего там может быть сложного. Бойлерплейта много получается в обоих случаях. Но код React, впрочем, немного посовременнее выглядит, однако JSX — это «вырви глаз».
Насчет подхода — очень похоже на Angular, но в последнем присутствует куда более мощная концепция директив и никакого HTML в контроллерах.
Ну, если речь про голый React, то по ходу дела даже голый Backbone и тот понятнее в итоге. Но в случае с Мореарти (название глаз режет :) получилось в самом деле компактно
Так к тому и вопросы, что эти задачи решаются при использовании любого формата модулей и нет никакой очевидной необходимости менять их в зависимости от масштаба/сложности проекта. AMD по определению поддерживает асинхронную подгрузку. Бандлы, очевидно, можно создавать с помощью r.js. Что касается ES6-модулей, то там вообще круто разделено само определение модуля и способ его загрузки — «бандлить» можно как угодно и транс-компилировать в AMD (если нужно) или в CommonJS (для Browserify-подобных упаковщиков).
К слову, сейчас транс-компиляция все равно идет в один из существующих форматов модулей в зависимости от того, каким образом нужно модули использовать. И если нужно что-то лениво подгружать — в итоге технически используются именно AMD-модули для этого.
А в чем проблема? Lazy-loading то можно реализовать. Я понимаю, как работает require, если ему передать строку — require('some-module') — он будет искать этот модуль, причем еще до загрузки основного модуля, который содержит такой вызов (парсит все содержимое функции в виде строки). А что насчет require(['module-name'], function (module) {… })? То, что возвращает загруженные модули — это понятно, более того, поддерживает их состояние.
Насчет собственных модульных систем — ни к чему, как по мне. Сам не особо рад оберткам в виду «define» для всех AMD-модулей (отчасти решается build-процессом), но тут еще вопрос совместимости с другими разработками и сторонними библиотеками. Ну и сейчас уже, похоже, время переходить на ES6-модули, где, наконец-то, определение модуля не зависит от того, как он будет загружаться.
AMD-модули асинхронны по-определению. Никто не мешает вызвать «require» внутри какой-нибудь функции, и загрузка модуля начнется только после этого (если не был ранее затребован и загружен каким-нибудь другим вызовом «require»).
Пример подхода можно посмотреть здесь. Особое внимание нужно уделить предотвращению повторных загрузок и параллельных загрузок одного и того же модуля.
Есть свои хакерскиеспособы, и даже работают, но я бы в большинстве случаев сам бы не советовал так усложнять.
Другой способ — когда мы изначально подключаем все компоненты Angular, но в каждом из них только по необходимости запускаем загрузку модуля, который непосредственно исполняет код. То есть, к примеру, в качестве контроллера создаем функцию, которая сначала все загружает, и только по факту загрузки выполняет нужную работу. Таким образом, приложение изначально «знает» обо всех своих компонентах, но не в курсе, что они на самом деле делают. Не самый гибкий способ, но также вполне работоспособный. Только надо аккуратно запускать $digest-цикл вручную.
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-файл приложения (то есть, шаблоны не загружаются асинхронно в продакшене).
Да отнюдь не миф — более того, еще и легко упаковывается в Cordova/PhoneGap и запускается на мобильниках (обычно спец-версия с дополнительными оптимизациями).
Не удобно хранить различные шаблоны для каждого языка приложения и править их все при малейшем изменении, поэтому они генерируются на сервере.
Для какого «каждого языка»? Шаблоны Angular-приложения — это шаблоны, которые доступны только этому приложению, это его view-слой.
Они могут отдаваться сервером в различном размере в зависимости, например, от размера экрана клиента, хотя и доступны по одному адресу.
Зачем? А media-queries? А если прямо отдельное что-то, например, для особых платформ, вроде старых мобильников — то проще делать отдельное приложение для них, нежели утяжелять/усложнять основное, поскольку самый камень преткновения, обычно, скрипты, а не разметка.
Ну, в идеале, если это отдельное приложение — оно вообще целиком статика, работающее с Web-API (http/websockets). И его проще воспринимать именно как отдельное приложение, а не как некую структуру, части которой генерируются на сервере. То есть, после загрузки страницы, все данные идут в виде JSON.
Стили я тоже не сторонник хранить разбросанными по приложению. Они вообще лежат за пределами папки приложения — у них просто сильно другая структура организации и очень много абстрактных/общих компонентов.
Насчет атрибутов — это одна из киллер-фич, однозначно. Никакие они не магические. Директивы, которые «цепляются» к атрибутам — это самый что ни на есть «unobtrusive JavaScript». Например, вы же можете добавить какой-нибудь атрибут, вроде 'data-tooltip=«Hi I am a tooltip»' к элементу, чтобы потом с помощью JS найти этот элемент и реализовать логику этого самого tooltip-а. То же самое и здесь, только возможностей куда больше. По сути, ни для чего, кроме вашего приложения такой атрибут смысла не несет в любом случае, а директиву можно привязать к любому из существующих элементов или атрибутов.
Насчет изучения множества новых концепций — согласен, порог входа низок, но чтобы сделать что-то реальное — вначале нужно сломать немало копий.
Во-первых, вопрос касается не только React — это относится к любым генерируемым на клиенте UI. А во-вторых — все современные поисковики поддерживают индексацию AJAX-контента (на примере Гугла, но и у Яндекса, и у Бинга тоже это есть). Кроме прочего, Гугл активно «учится» выполнять JS, ну и напоследок — задача индексации замечательно решается, просто нужен другой подход — сервисы, вроде Prerender.io именно для этих целей.
Насчет подхода — очень похоже на Angular, но в последнем присутствует куда более мощная концепция директив и никакого HTML в контроллерах.
И если уже работаете с ES6-модулями, то зачем Browserify?
Насчет собственных модульных систем — ни к чему, как по мне. Сам не особо рад оберткам в виду «define» для всех AMD-модулей (отчасти решается build-процессом), но тут еще вопрос совместимости с другими разработками и сторонними библиотеками. Ну и сейчас уже, похоже, время переходить на ES6-модули, где, наконец-то, определение модуля не зависит от того, как он будет загружаться.
Пример подхода можно посмотреть здесь. Особое внимание нужно уделить предотвращению повторных загрузок и параллельных загрузок одного и того же модуля.
Другой способ — когда мы изначально подключаем все компоненты Angular, но в каждом из них только по необходимости запускаем загрузку модуля, который непосредственно исполняет код. То есть, к примеру, в качестве контроллера создаем функцию, которая сначала все загружает, и только по факту загрузки выполняет нужную работу. Таким образом, приложение изначально «знает» обо всех своих компонентах, но не в курсе, что они на самом деле делают. Не самый гибкий способ, но также вполне работоспособный. Только надо аккуратно запускать $digest-цикл вручную.
Папки:
Пара замечаний:
— Во время запуска build-процесса в корне создается папка dist. Ее структура повторяет папку проекта в целом, но содержит оптимизированные версии js/css-файлов.
— Для production все шаблоны собираются в отдельный js-файл, являющийся AMD-модулем, содержимое которого включается в основной JS-файл приложения (то есть, шаблоны не загружаются асинхронно в продакшене).
Для какого «каждого языка»? Шаблоны Angular-приложения — это шаблоны, которые доступны только этому приложению, это его view-слой.
Зачем? А media-queries? А если прямо отдельное что-то, например, для особых платформ, вроде старых мобильников — то проще делать отдельное приложение для них, нежели утяжелять/усложнять основное, поскольку самый камень преткновения, обычно, скрипты, а не разметка.
Стили я тоже не сторонник хранить разбросанными по приложению. Они вообще лежат за пределами папки приложения — у них просто сильно другая структура организации и очень много абстрактных/общих компонентов.
Именно это он и обеспечивает при необходимости. Ну это так, к слову :)