JavaScript-only: гомогенная архитектура веб-проектов

Работа фронтенд-разработчика наполнена задачами по оптимизации кода, переносу готовых фрагментов между версиями проектов и т.п., сложность которых зачастую определяется исторически сложившимся подходом к самой разработке. В своём докладе на конференции HolyJS, которая пройдет 5 июня в Санкт-Петербурге, фронтенд-разработчик Алексей Иванов расскажет, как объем этих проблем можно сократить отказом от привычного подхода, когда приложение состоит из разрозненных частей, в пользу «всё-в-JS». Мы же в преддверии конференции поговорили с Алексеем о том, от каких именно сложностей избавляют предлагаемые им идеи (сами идеи будут подробнее раскрыты в докладе).

40888fcb55344843a3786f48b4d29a70.jpg

— Расскажи вкратце о себе и своей работе.

— Меня зовут Алексей Иванов, я фронтенд-разработчик в компании «Злые марсиане» (Evil Martians). Это распределенная группа разработчиков, помогающая крупным компаниям, вроде eBay или Groupon, а также различным стартапам в короткие сроки и без проблем запускать интернет-проекты с расчётом на быстрый рост.
В Марсианах я сейчас занимаюсь фронтендом сервиса под названием eBay Social для российского офиса eBay. Это классическое приложение на Ruby on Rails с отдельными интерактивными частями, написанными на React.
До Марсиан я делал первую версию SPA-приложения для ridero.ru на Backbone, помогал запускать пару сервисов для Яндекса с использованием bem-tools, а также занимался разработкой других серверных и SPA-приложений разного размера, что позволило мне потрогать кучу разных инструментов и методологий разработки. Мне нравится изучать и сравнивать разные способы организации кода, работы с зависимостями и разрешения конфликтов, используемые в разных методологиях и инструментах.

Откуда вообще появилась идея о каком-то глобальном изменении подхода к разработке?

— Фронтендеры Марсиан работают с двумя основными типами проектов.
Во-первых, мы создаем классические проекты на Ruby on Rails. В таких проектах рендер шаблонов происходит на сервере внутри самих Rails, а сборка CSS и JavaScript живет отдельно в Node.js и Gulp’е или другом сборщике. Мы пользуемся  пре- и постпроцессорами, собираем отдельные файлы в общие бандлы и сжимаем код Clean CSS и UglifyJS, но при этом CSS и JavaScript друг о друге знают очень мало, а про HTML, с которым они работают, вообще ничего не знают.
Во-вторых, мы создаем одностраничные приложения (SPA), которые строят HTML, а часто и CSS сразу в браузере, и с сервером общаются только на уровне данных.
По сути, это два параллельных мира в современном фронтенде. В какой-то момент SPA отпочковались от классических серверных приложений в отдельную эволюционную ветку и пошли собственным путём со своим набором мутаций и новых идей. В результате набор инструментов, который используется на этих двух типах проектов, сильно различается.

Инструменты и подходы, используемые в современных SPA, позволяют с минимальными усилиями решать многие классические проблемы фронтенд-разработки: пересечение названий переменных и CSS-классов в глобальном пространстве имен, удаление неиспользуемого кода и стилей, создание сборок CSS и JavaScript под конкретные разделы и страницы. Приятнее всего то, что основную часть работы тут за вас делает машина при вашем минимальном вмешательстве.

Поэтому возвращение к классическим серверным приложениям после работы с SPA вызывает боль и страдания. Вещи, которые в SPA работали из коробки, теперь либо никак не работают, либо требуют для реализации кучу времени и сил.

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

— Можешь ли ты привести примеры упомянутых проблем, свойственных разработке серверных приложений?

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

Допустим, мы отправляем пользователю HTML-страницу, CSS и JavaScript. CSS у нас написан по методологии БЭМ. Один из способов оптимизации — сокращение длинных имен в CSS, которые мы при разработке написали для себя, чтобы избежать конфликтов с другими классами.

Предположим, у нас есть класс:

.block__element_modificator {}


И таких строчек у нас порядка нескольких тысяч. Хочется вместо них отправлять нечто менее длинное, допустим:

.b1 {}


Как нам это сделать?
Классы CSS у нас используются в нескольких местах: во-первых, в самом CSS-файле, во-вторых, в HTML, и, в-третьих, в JavaScript.
Начнём с CSS-файла. Первое, что придется сделать, — это объединить CSS-файлы в один, так как если мы займемся заменой в разных файлах, то не сможем быть уверенными в отсутствии конфликтов (технически сократить названия можно и без объединения в один файл, но необходим будет промежуточный список замен, который придется подгружать при обработке каждого нового файла; данный вариант реализовать сложнее и затратнее с точки зрения необходимых ресурсов, — прим. ред.). Далее мы пройдемся по полученному файлику какой-нибудь программкой и получаем на выходе две вещи: файл с замененными именами и список замен в виде:

{ 'block__element_modificator': 'aBc' }


Пока всё просто. Идём дальше.
Теперь нам нужно заменить эти классы в HTML. Это уже не так просто: классы могут собираться из отдельных кусков строк, переменные в шаблонах могут быть частью классов, собирать имя класса из этих частей мы можем не внутри аттрибута class, а где-нибудь отдельно и т.д. Ну и где-то у нас может на теге использоваться один класс, а где-то — несколько. Нам нужно выявить все эти моменты:


Если мы пропустим хоть одно такое место, верстка сломается.

Кроме этого, названия классов мы используем в JavaScript. Там определить, что именно у нас является именем класса, еще сложнее:

var className = "block__element_modificator";
$elem.addClass(className);


но не править ничего лишнего:

var block = ...;


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

В итоге простая задача — сокращение имен классов — у нас превращается во что-то нетривиальное.

При всем этом сжатие имён классов дает нам не очень большой выигрыш по размеру. Есть более эффективные способы оптимизации размера стилей — например, очистка файлов от неиспользуемых правил. При использовании библиотек вроде Bootstrap или иконочных шрифтов вроде Font Awesome в сборку попадают сотни неиспользуемых правил. Точно так же вес добавляют правила пропущенные и неочищенные при рефакторинге. Если бы мы могли отправлять в браузер только реально используемые правила, это дало бы нам гораздо больший выигрыш в размере.

— Убирать из сборки неиспользуемые элементы так же сложно? И действительно ли этот «исторический хвост» дает большой прирост к размеру сборки?

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

.news .title {}


У нас на сайте могут использоваться отдельно и класс news, и класс title. При этом в такой комбинации, как и в правиле, они могут никогда не встречаться. В этой ситуации правило тоже можно спокойно удалить. В результате для удаления неиспользуемого кода нам нужно понять структуру страницы, причем не только в текущем состоянии, а во всех возможных (страница может быть для авторизованного или неавторизованного пользователя, с попапом, персональной рекламой или выборкой записей определенного типа в ленте друзей). Стоит учесть, что для понимания структуры страницы нам одного HTML не достаточно, т.к. у нас есть JavaScript, который умеет все это изменять. По-хорошему, после того, как мы построили дерево возможных состояний для страницы, нам нужно понять, как именно наш JavaScript может это дерево изменять.

И вот только после того, как мы все это поняли, мы можем со спокойной совестью удалять правила из CSS.

То же самое касается сокращения JavaScript. Мы можем в автоматическом режиме удалить неиспользуемые переменные и функции. Но код, касающийся работы с HTML, мы так удалять не можем, потому что без знания структуры страницы мы не имеем возможности понять, что из него нам правда нужно, а что никогда не будет использовано.

— А нельзя ли просто использовать какой-то другой подход при оптимизации?

— Мы можем попробовать зайти с другой стороны. Например, разбив общий бандл CSS и JavaScript на отдельные бандлы для разных страниц, чтобы пользователю отправлялась только то, что нужно для текущей страницы. Допустим, он пришел на главную страницу — давайте отправим ему только то, что необходимо для главной, чтобы она казалась быстрее, а остальное загрузим как-нибудь потом. Для упрощения мы можем даже не по страницам сборки делать, а по состоянию пользователя — для авторизованного или неавторизованного (т.е. часть добавлять уже после авторизации).

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

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

— Неужели никакого инструментария для решения этих проблем не существует? Как же тогда выйти из этого «замкнутого круга»?

— Вначале интервью я как раз говорил, что во фронтенде сейчас существует два параллельных мира: мир серверных приложений и мир SPA. В мире SPA многие из описанных выше проблем успешно решены, и о них никто не вспоминает, а вот в мире серверных приложений они актуальны до сих пор.

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

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

— Идея, вроде бы, лежит на поверхности. Почему же она не идёт в массы?

— Идея ещё как идет в массы. Есть изоморфные приложения и попытки скрестить React с Django и Rails. Есть bem-tools, поводом для создания которого были многие из проблем, о которых я буду рассказывать в своем докладе. Есть попытки подружить HTML и CSS на сервере через pstcss-modules, который написал мой коллега Саша Мадянкин. То есть, подходов к проблеме с самых разных сторон много, хотя и нет одного общего популярного общепризнанного решения.

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

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

Благодарим за беседу!

В рамках беседы мы вкратце описали проблемы, с которыми ежедневно сталкиваются фронтенд-разработчики. О том, как именно подход «всё в JS» может упростить ситуацию, слушайте в докладе Алексея Иванова на HolyJS. Кроме этого, на конференции будет несколько других докладов, связанных с архитектурой веб-приложений. Ну и разумеется, нашему сегодняшнему собеседнику можно будет там же, не отходя от кассы, задать интересующие вас вопросы.

© Habrahabr.ru