Javascript-фреймворки: должен остаться только один

в 0:24, , рубрики: AngularJS, javascript, javascript framework, Meteor.JS, ReactJS, Блог компании Конференции Олега Бунина (Онтико), сергей аверин, метки:

Сергей Аверин (
XEK )

Сергей Аверин

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

Про что же, все-таки, получился доклад? Доклад про то, как выбирали новый фреймворк, почему выбирали, и какие задачи решали.

Javascript-фреймворки: должен остаться только один - 2

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

А вот теперь настало время очешуительных историй.

Немного про компанию:

Javascript-фреймворки: должен остаться только один - 3

Acronis — очень большой российский бизнес, который обслуживает 5 млн. пользователей, из которых примерно процентов 10 — это корпоративные клиенты, которым их работы покупают лицензии. Соответственно, компания сама по себе тоже очень большая, у нас 17 разных офисов, RND находятся в трех разных странах, и самый большой RND находится в Москве. Но при этом разработка у нас распределенная, и есть удаленщики, которых я здесь не посчитал.

Масштаб разрабатываемого софта самый разный: есть у нас коробочный софт под Windows, есть коробочный софт с веб-интерфейсом, который мы продаем на предприятия, есть софт, который мы сами продаем и сами внедряем, и это смесь и коробочного и cloud-софта. Есть и cloud-софт, который мы продаем по подписке и домашним пользователям, и корпоративным. Есть даже софт, который мы продаем сначала крупным хостинг-компаниям, которые уже от своего имени продают его под своим брендом их конечным юзерам. И у всего этого дела есть огромное количество веб-интерфейсов. Речь идет именно о веб-продуктах, а не о веб-сайтах, как таковых.

Javascript-фреймворки: должен остаться только один - 4

Когда я пришел в компанию, стал разбираться, чего же у нас есть, как же все происходит, выяснилось, что внутри компании есть веб-админки разного рода, которые мы делаем сразу на куче разных технологий, начиная от true-фронтенд типа Ext JS или AngularJS, заканчивая сайтами, которые пишутся на Ruby on Rails и в которых jQuery просто переключает страницы или графики.

Перед коллективом стояли такие проблемы.

Javascript-фреймворки: должен остаться только один - 5

Во-первых, технологий много, они все разные, разработчиков из одного отдела в другой не перетащить, не помочь какому-то проекту, потому что Ruby on Rails разработчики, которые пишут на jQuery, про AngularJS ничего не знают. Во-вторых, выяснилось, что существенное количество людей работают part-time — то бэкенд-разработчиками, то фронтенд-разработчиками. У нас, например, есть ребята, которые на PHP пишут, и они же делают на AngularJS сайт, который работает с ним как с бэкендом. Их не очень много, у них проект маленький, и смысл заводить им отдельного фронтенд-разраба отсутствует.

В итоге я насчитал, что у нас есть java-люди, ruby-люди, python-люди и php-люди, которые все делают фронтенд. И при этом в компании нет верстальщика, ни одного. Т.е. есть несколько людей, которые экспертно знают эту область, но обычно они работают кодерами на AngularJS. В итоге в этих проектах совершенно разный стиль кода, совершенно разный стиль комментариев, они по-разному деплоятся, в каком-то месте, с помощью Ruby on Rails мы собираем AngularJS приложения с помощью ruby‘шного сборщика. И в итоге — везде разный зоопарк. В каждом отделе зоопарк свой. А еще, короче, получается так, что наш самый флагманский продукт, в котором написан Ext JS, это огромная админка, там сотни экранов, он сейчас построен на базе версии 4, а в 2015-ом году, совсем недавно, зарелизилась уже версия 6 этого фреймворка и, вроде как, мы отстаем аж на 5 лет от версии фреймворка, которую мы используем. Возможно, тоже надо что-то с этим делать.

Пришло, конечно же, начальство и поставило задачу. Задача была дана сверху:

Javascript-фреймворки: должен остаться только один - 6

Нам нужен толстый клиент на фронтенд-технологиях, который будет общаться со stateless backend API максимально везде, где мы можем это внедрить. Нужна единая библиотека UI-компонентов, единый look and feel для всей компании, потому что мы немножко страдаем от того, что у нас шрифты где-то разные, где-то по пикселям верстка поплыла, и в итоге получается так, что у нас два соседних отдела рисуют какие-то общие элементы для всех наших админок, которые дизайнерами нарисованы одинаково, немножко по-разному — где-то верстка на HTML, где-то верстка запихана в JS-код в каком-то собственном шаблонизаторе. И CSS-слой тоже принципиально разный, где-то он накладывается из трех-четырех файлов поверх друг друга, заскиневая существующих фреймворк.

Очень хочется, чтобы мы, наконец-то, могли подключить нормального верстальщика и нанять разработчика с меньшей зарплатой, который бы отвечал, с одной стороны, за общение с дизайнерами, подготовку дизайнов, а с другой стороны java-скрипторам приносил и говорил: «Вот, они хотят этого, здесь нужно анимировать этот интерфейс…». И при этом мы бы могли нанимать JS-кодеров в проекты не топового уровня. Сейчас у нас работает достаточно много ребят, которые прошли огонь и воду, они знают Java, они знают .Net, и в этом плане для них любой javascript-фреймворк — это не проблема, потому что они, в принципе, очень опытные. Поэтому хочется как-то иметь возможность нанимать не одного из ста, а хотя бы двух из пятидесяти.

И есть такой еще отдельный pqrity. У нас большая часть людей, которая делает фронтенд, это либо бывшие, либо текущие бэкендеры, поэтому очень хочется, чтобы все это было понятно бэкенд-разработчикам. Менеджмент, когда видит код на JS, он начинает говорить, что «Блин, мы ничего не понимаем, все плохо, давайте перепишем», а зачастую все это не так.

Javascript-фреймворки: должен остаться только один - 7

Посмотрели, что же, на самом деле, мы имеем в компании. Оказалось, что у нас есть партнерский продукт, который написан на Dojo. Для него мы пишем плагины на Dojo, это очень древний фреймворк, очень особенный, он работает как компонентная модель. Это не совсем MVS, но с этим мы решили ничего не делать, потому что мы не можем никакой другой фреймворк использовать, иначе мы не сможем с этой партнеркой работать.

Для сайта, собственно, у нас почти нет никакой динамики. Нам важно, чтобы его индексировали поисковики, и нам важно, чтобы он работал быстро. Нам там Javascript особо не нужен. Поэтому какой-то суперфреймворк для него, вообще, не уперся. Это задача, которую мы не должны решать.

Из оставшегося, из чего на самом деле делаются админки, это Angular JS первой версии, это Ruby on Rails, плюс генерирующие страницы на бэкенде, плюс какие-то jQuery-дописки, которые делают некую динамику. И большая часть продуктов написана на ExtJS 4. Это очень высокоуровневые фреймворки, которые больше всего напоминают .Net или CuTI, на котором большая часть продуктов компании — серьезных и взрослых — написана.

Javascript-фреймворки: должен остаться только один - 8

Полез я разбираться, что же такое этот ExtJS. Оказалось, что это огромная махина, локомотив кода. На главной странице 395 классов этого фреймворка описаны.

Javascript-фреймворки: должен остаться только один - 9

Каждый класс имеет очень глубокое дерево наследования, т.е. в этом плане очень сильно слизано с того, как строится CuTI, .Net и прочие MVC фреймворки, которые работают где-то в винде или в линуксе под десктоп. В итоге получается, что зачастую код, который ты дебажишь, находится где-нибудь на три уровня выше, чем тебе сейчас надо. Разобраться в этом действительно сложно.

Класс Ext.panel:

Javascript-фреймворки: должен остаться только один - 10

Это только методы из него. Там еще столько же событий, столько же свойств разных. Совершенно понятно, что ни один разработчик запомнить все эти методы не может. Поэтому постоянно приходится работать с документацией, чтобы понять, как сделать даже самую простую штуку, потому что размер какого-нибудь компонента… Там есть аж три разных метода, и они три разных размера возвращают — каждый по-своему.

Javascript-фреймворки: должен остаться только один - 11

В итоге мне было печально, очень. Я первое время вообще ничего не понимал, смотрел на все это дело и задавал себе такой вопрос: «Господи, зачем же вы все это написали?».

Индексная страница флагманского приложения была мною напечатана на листах А0 и занимала 51 лист шрифтом 6.

Javascript-фреймворки: должен остаться только один - 12

Вот увеличенное это дело:

Javascript-фреймворки: должен остаться только один - 13

Это DOM-дерево, в нем было 32 с чем-то тысяч нод. Это просто индексная страница, сгенерированная фреймворком, т.е. мы еще ни на одну кнопку не нажали. Это просто загрузилась страница и все отрисовала. Стало очевидно, что, блин, что-то тут странное происходит, явно есть какая-то фигня с этим.

Javascript-фреймворки: должен остаться только один - 14

Полезли смотреть, как же делается UI в этом фреймворке. Оказалось, что пользователь получает сразу библиотеку UI компонентов, которая скинится CSS’ом. Ты должен CSS’ом дописать поверх, тогда получишь свой кастомный скин. И много компонентов, которых не было, их пришлось реализовать с нуля. В итоге зачастую, не везде, но очень часто, я натыкался на что-нибудь такое. Это javascript класс, который с одной стороны UI компонент, но с другой, зачастую видно, что в нем аж три разных сущности смешивается — это javascript-код, html-код и это какие-то очень странные директивы местного шаблонизатора, эти xindex, xcount или этот tipeof values string, запиханные в фигурные, квадратные скобки…

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

Javascript-фреймворки: должен остаться только один - 15

Дальше выяснилось, что это единственный видимый мною фреймворк, в котором есть такая шутка, как layouting. Полезли посмотреть, что же, как же, почему, откуда эти теги? Оказалось, что при изменении размеров окна, при изменении размеров какого-то компонента фреймворк так же, как это на декстопе, вычисляет сам размер блоков и вставляет их inline CSS’ом. Типа left, right, width, height. Мало того, там очень хитрый алгоритм, который берет текст и оценивает его размеры, запихивает его в невидимый div, и смотрит, какого размера этот div получился при заданной ширине. И так он мимикрирует под функции оценки размеров текста, сколько этот текст займет в этом блоке, и вычисляет эти блоки. А потом выяснилось, что алгоритм этот, еще и древовидный, т.е. он по 6-7 проходов делает, потому что он, как браузер, тоже натыкается на какой-то компонент, который не влазит, начинает его увеличивать и родительский компонент тоже пересчитывать. В итоге, начинаешь двигать правую страницу браузера и четко видишь затупы его про изменения размера этого фреймворка. Но, с другой стороны, это дает тебе типа бенефис, что во всех браузерах это работает абсолютно одинаково, и про проблемы совместимости между браузерами ты не паришься, потому что максимально тебе этот вопрос закрыли.

Javascript-фреймворки: должен остаться только один - 16

Попутно выяснилось, что компонентов очень до фига. И все эти компоненты, каждый из них — это инстанс javascript’ового класса, у которого от трех до пяти уровней наследования, а то и до семи-восьми. Для каждого из них этим волшебным layout’ером оцениваются размеры, они высчитываются и выставляются. У родительских потом высчитываются размеры, и в итоге все это дело генерирует такие веселые штучки — inline css’ы, которые вставляются прямо в div’ы этих компонентов, прямо в теги. Поэтому получается такое огромное DOM-дерево, странное.

Javascript-фреймворки: должен остаться только один - 17

В попытке понять, как же этот layouting работает, я наткнулся на такое совершенно страннейшее место.

Javascript-фреймворки: должен остаться только один - 18

В этот момент мне стало совсем плохо, и я понял, что я не пойму никогда, как это происходит.

Javascript-фреймворки: должен остаться только один - 19

Вы все еще помните, что речь идет о производительности?

Javascript-фреймворки: должен остаться только один - 20

Все это дело занимает почти 2 Мб уже в пожатом виде, поэтому совершенно неудивительно, что на каких-то тестах типа «а как это будет работать на мобиле?» выдает веселые времена загрузки. Но, тем не менее, это не такая большая проблема для нас, потому что большинство клиентов корпоративные, и они лезут в админку, когда им нужно либо что-то настроить, т.е. в первую неделю после покупки продукта, либо когда адский пипец, т.е. нужно достать бэкап. И человек, у которого адский пипец, готов подождать 16 секунд, пока, наконец, загрузится админка. Тем более, здесь речь идет о достаточно тонком канале.

Javascript-фреймворки: должен остаться только один - 21

С фреймворком понятно, очень сложно и запутано. А сам приложение? Полезли в код приложения.

Javascript-фреймворки: должен остаться только один - 22

Получается так, что в коде мало комментариев. Какие-то трудные места не объяснены. Все приложения жестко связаны. Вместо одного класса — длинного, на 2,5 тыс. строк, мы имеем дело обычно с тремя-четырьмя классами по 1000 строк, которые очень жестко знают друг про друга и так по кольцу, общаясь, пересылают себе события, меняют состояния внутри себя. И у нас внутри очень большие проблемы с границами между моделью и бизнес-логикой, и между бизнес-логикой и view. Получается так, что у нас есть view-классы, которые называются как-нибудь типа «волшебная реализующая определенный функционал панель», которая и одновременно отрисовывает себя и отрисовывает дочерние компоненты, и еще у нее до фига бизнес-логики, которая умеет переключать какие-то панели внутри, и которая умеет отсылать уведомления, даже зачастую они из view-классов лазят на сервер.

Javascript-фреймворки: должен остаться только один - 23

Поэтому (очень условно) получилось так, что у нас такой не mvc, а M+CV. Т.е. граница между контролем и view очень не определена. Получилось так, что и в моделях есть бизнес-логика, и в контроллерах есть бизнес-логика, и во view есть бизнес-логика, и UI логика, и состояние приложения тоже размазано по всем трем видам классов. Поэтому трудно, например, понять откуда пришли эти данные, или кто является авторитативной точкой, откуда эти данные взялись, и кто хранит на текущий момент самую достоверную копию этих данных, потому что она есть в нескольких частях приложения.

Javascript-фреймворки: должен остаться только один - 24

А еще получилось так, что сам фреймворк Ext JS всех очень толкает к архитектуре построенной на publish/subscribe, потому что, в принципе, у него все UI компоненты завязаны на этот механизм, и программисты невольно выбирают то, что предлагает фреймворк. И, вроде как, publish/subscribe — правильный паттерн, но когда ты начинаешь смотреть на приложение, в котором типа 100 страниц, в итоге получается, что ты пытаешься разобраться: «Блин, а к чему же приводит нажатие вот этой кнопки в этой панели, и что же она дергает?».

Javascript-фреймворки: должен остаться только один - 25

И выясняется, что, во-первых, события могут неявным образом попадать в родительские классы, которые, в свою очередь, генерят вторичные события, которые попадают в какие-нибудь классы с бизнес-логикой, которые в свою очередь генерят еще события. Затем часть событий игнорируется фреймворком, потом меняется какая-то модель, эта модель генерирует события о том, что «данные во мне поменялись». Появляется еще пяток классов, которые подписаны на ее изменения, и в себе меняют какое-то состояние, которое зависит от состояния этой модели.

В итоге, через три часа ты, наконец, строишь стройную картину и понимаешь, что у всех событий названия очень похожи, и везде есть onclick. И тебе нужно понять, это onclick от этой кнопки, которую ты нажал или onclick от соседней кнопки, которую ты нажал. Потому что фреймворк не запрещает подписку любого компонента на любой. Т.е. ты можешь добраться из кого-нибудь суперконтроллера до кнопки в конкретной панели и подписаться на нее onclick. И это будет просто onclick в кавычках. И такие дела очень трудно дебажить, потому что нет какой-то уникальной селективности этих компонентов. Т.е. там есть свой механизм, называется ComponentQuery, похожий на XPath, в котором можно написать RegEx-запрос, который тебе говорит: «Найди мне там все кнопки, которые вложены в определенную панель с определенным названием». Поэтому, когда ты пытаешься чего-то отрефакторить, очень трудно найти все, вообще, места, где мы реагируем на конкретное событие. Зачастую у нас это даже приводило к циклам событий, когда приложение зависает, потому что постоянно появляются дочерние события, оно начинает их обрабатывать, и этот цикл бесконечен. Пока ты не напишешь какой-нибудь хитрый if типа «если ко мне это событие уже приходило, я его не обрабатываю», то мы дальше его не обрабатываем. Т.е. речь идет о запутанной архитектуре.

Все нормально.

Javascript-фреймворки: должен остаться только один - 26

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

Javascript-фреймворки: должен остаться только один - 27

Вот так вот получилось. Причем непонятно, на самом деле, почему получилось. Потому что, на мой взгляд, могло быть лучше. Но, короче, объекты Arrays мы проверили, остальное — как получится. И вечная эта история про то, что когда ты начинаешь писать тесты, ты многое узнаешь про свое приложение, что на самом деле ни фига не надо было делать такое количество связей, потому что замокать 15 объектов ради вызова одной функции, очень геморройно. И в этом плане получили тесты «давай, до свидания».

Какие выводы? Как бы, вот такие, конечно, выводы…

Javascript-фреймворки: должен остаться только один - 28

Но на самом деле, настоящие выводы в чем?

Javascript-фреймворки: должен остаться только один - 29

Что фреймворк очень сложный для понимания разработчиков, особенно разработчиков, которые до этого не защищали докторскую диссертацию по Java. Во-вторых, код, который получился, очень запутанный. В нем очень много связей, все классы друг про друга знают, и события, которые появляются, никем не контролируются, и это приводит к фейерверко-образным взрывам изменения стейтов приложения, когда 15 мест подписаны на какой-нибудь стейт, события потом генерируют свои дочерние события, и еще какие-нибудь другие места начинают обрабатывать эти события… В этом плане фреймворк жутко асинхронный, и эта асинхронность взрывает мозг. Получается так, что компоненты правят люди, которые понимают одновременно язык шаблонизатора, намешанный JS-код, html, CSS и все это в одном файле. Обычного верстальщика нанять? Я не знаю… Сколько сейчас получает верстальщик? За 40-60 тыс. невозможно найти верстальщика и посадить его за простую рутинную работу «в этой панели убери эту кнопку», потому что сборка всего UI происходит в JS-коде методом созданий инстансов JS-классов. Т.е. ты описываешь большое дерево, какую кнопку в какую панель вложить, какие параметры передать, какие ссылки, на какие события потом подписаться.

Соответственно, главный мой вопрос был — виноват ли в этом фреймворк? На самом деле, конечно, виноват. Частично. Потому что он подталкивает тебя к publish/subscribe, потому что он подталкивает тебя к такому mvс, который они сами делают, псевдо mvc. Потому что в нем нет dependency injection из коробки, потому что он не говорит тебе, как тестировать это все дело…

Javascript-фреймворки: должен остаться только один - 30

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

Нужно четкое разделение работы по людям: одни люди делают верстку, другие люди в верстку вдыхают что-то, третьи люди ответственны за работу с API, четвертые– с дизайнерами. Нужно разделение языков и технологий, желательно по-файлово, чтобы можно было каким-то людям, которые не разбираются в JS-коде давать какие-то вторичные задачи, например, переводы, т.е. локализацию на нужный язык.

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

Javascript-фреймворки: должен остаться только один - 31

К чему главный посыл всей этой презентации (а это самый главный слайд) — что все ваши песни про производительность фронтенда превращаются в лапшевидный код, отсутствие стандартов и «мы наняли нового чувака, а он четыре месяца правит одну багу». Давайте сначала улучшим производительность разработчиков, дадим им в руки нормальные, а не гнутые инструменты, и они сделают код, который будет намного лучше работать, потому что эти задачи были решены на уровне фундамента. Т.е. мы будем стоять на плечах гигантов.

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

Конечно, я полез в Интернет. Ну, а что делать? Тут все бы полезли в Интернет.

Javascript-фреймворки: должен остаться только один - 32

Выяснилось, что за пять лет, пока я управлял и работал на PHP, в мире JS многое поменялось, в этом плане пришлось многое выучить заново.

Javascript-фреймворки: должен остаться только один - 33

И эти тысячи фреймворков, каждый из которых как две капли воды зачастую похож на друг друга, особенно во flux фреймворках — это прямо «придумай свое название точка JS». И из этого ада там, где Google Web Toolkit уже давно умер, надо как-то выбрать. Блин, что делать? Конечно, надо было что-то подумать, как бы посмотреть, как другие где-то что-то выбирают в Интернете, залезть и где-то спереть результат.

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

Javascript-фреймворки: должен остаться только один - 34

Именно про компании и сайты я оттуда слайды вырезал, остаются именно честные цифры. Это количество строчек кода, написанных вообще с момента появления Github на разных технологиях. Плюс-минус.

Javascript-фреймворки: должен остаться только один - 35

Это слайд рассказывает о том, сколько было докладов по каким технологиям на каких крупных конференциях, посвященных именно фронту, в 2015 году за рубежом.

И везде картинка разная.

Javascript-фреймворки: должен остаться только один - 36

На этой картинке тренд популярности запросов в гугле. Их тут несколько… Здесь, если присмотреться, видно, что все технологии идут на самом деле на спад, кроме React js и Angular js.

Javascript-фреймворки: должен остаться только один - 37

Если взять какие-нибудь старые более-менее фреймворки дипа Dojo, Yahoo ui и нашего любимого Ext js, то тоже все идет на спад. В этом плане с React js и Angular js никто не поборется, потому что это единственное, что идет вверх.

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

Javascript-фреймворки: должен остаться только один - 38

Это американская компания, которая делает и продает cloud IDE. Получилась вот такая картина, совершенно другая. Т.е. те разработчики, которые поддерживают продукты, у них еще силен, например, Backbone, который по большому счету устарел, но, тем не менее, есть много проектов на нем, которые еще доделывают.

Javascript-фреймворки: должен остаться только один - 39

А это уже российский рынок. Мы пытались оценить, кого же мы можем нанять, потому что очевидно, что мы не в космосе работаем, и нам нужно делать продукты в компании, продуктов много и планов — громадье, поэтому кого-то надо нанять. Здесь, вообще, картина другая. Выяснилось, что так или иначе с какой-то из версий ExtJS (поскольку он достаточно долог) и с какой-то из версий Backbone (поскольку он тоже родился в 2008 году) так или иначе кто-то имел дело. Т.е. это никакой гарантии не дает на то, что этот человек прямо сейчас готов чего-то делать realtime на этом фреймворке, но, тем не менее, выяснилось, что среди растущих технологий React js и Angular js, появились внезапно Backbone и ExtJS, причем вакансии по ним есть, и их достаточно много. Вакансий по Backbone больше, чем со знанием React. Вот какая удивительная картина на российском рынке.

Javascript-фреймворки: должен остаться только один - 40

В итоге мы все эти исследования максимально скрестили и оставили то, что нам интересно. Туда добавили еще Dojo и добавили ExtJS 6, на который изначально хотели переходить. А Dojo, потому что его используют Parallels, с которым мы тесно интегрируемся, и на котором у них кое-что написано.

Javascript-фреймворки: должен остаться только один - 41

Здесь нагло было вырезано пять слайдов. Я не буду рассказывать про то, почему нам это все не подошло, потому что рассказ там очень скучный. Я не буду на этом останавливаться. Если вы полезете изучать для себя, то сформируете и какую-то картину для себя. Мы оценивали, исходя из наших задач. Одна из задач была в том, что технология должна быть более-менее встраиваемой по частям в существующий код, т.е. нам нужен не монолитный фреймворк, а, скорее, набор каких-то методик и библиотек, которые мы можем потихоньку внести. В этом плане оказалось, что Knockout — это вообще не фреймворк, а UI библиотека, Dojo — это просто мамонт, а ExtJS 6, хоть и привносит новую архитектуру, но на самом деле мало чем отличается от ExtJS 4. Т.е. там появилась поддержка какая-то, более хорошая работа с мобильными устройствами, но концепция не поменялась. Там чуть лучше стала архитектура, но для нас, как мы оценили, примерно одинаковая, что мы на ExtJS 6 будем переписывать честно архитектуру, прямо так, как они предлагают, что мы будем на любом другом фреймворке переделывать — примерно одинаково по затратам. В итоге отказались, потому что все тот же UI слой, все тот же publish/subscribe.

Истинных кандидатов, на самом деле, было только два.

Javascript-фреймворки: должен остаться только один - 42

Первый — AngularJS — по понятным причинам, который еще очень активно отстаивали и несколько отделов компании, и некоторые внешние советчики. Но с ним возникла очень интересная дилемма. С одной стороны, это выглядело действительно очень хорошим решением, с другой стороны, я видел очень много статей на Хабре и, вообще, в англоязычном Интернете, в которых обсуждают некие допущенные при его проектировке именно концептуальные ошибки, приводящие к тому, что все, как бы, очень круто, но мы запутались.

Наше собственное зрение на это дело такое, что если брать версию 1, на которой у нас в компании есть два проекта, и посидеть-покурить исходники, то у этого фреймворка реально хорошая модульность разделения на отдельные кусочки, каждый из которых делает свой маленький функционал — вот это очень полезно и хорошо. Но нет единого стиля раскладки по файлам, нет единого стиля именования каких-то переменных и единого стиля форматирования кода, т.е. это та вещь, которую хотелось бы как-то получить до кучи с фреймворком, но ни фига не получится. И со временем этот стиль раскладки меняется. Также от себя добавлю, что зачастую находятся места в коде, которые по-хорошему должны бы быть отдельными классами, но почему-то их в виде объекта с функциями туда пихают. Трудно дебажить, очень много разной хитрой «магии», очень сложно интегрировать с какими-то новыми технологиями, потому что стандарты абсолютно все свои… Как собирать это дело — тоже не очень понятно, однако этот вопрос можно решить. Но самая беда, что начальству я это продать не мог, потому что мы все говорили, что «Ребята, они делают новую версию, и код будет несовместим». Что делать дальше — непонятно.

Javascript-фреймворки: должен остаться только один - 43

ОK, полезли смотреть версию 2. Версия 2 прямо вообще отлично выглядит, как то, что нужно, такой бэкендно-ориентированный подход, все разложено по полочкам, хороший очень синтаксис на TypeScript — то, что надо. Но, блин, нет шансов на релиз, вообще, нет. Я, самое главное, не понимаю, эта супер-мега гугл корпорация, она давно работает с python… Пример того, как python переходит с версии 2 на 3 — это просто хрестоматийный пример того, как надо это делать. Надо поддерживать синтаксис старой версии, надо быть backword compatible. А здесь какой-то адский ад просто. Он будет несовместим. «Мы сейчас что-то новое делаем. Вот альфа-версия, для нее ни хрена нет документации. Когда мы запустимся — непонятно и, самое главное, у нас во всех трех языках еще разный синтаксис». Ну, зашибись, блин!

Это что, это решение? Вы извините меня, это локомотив фронтенд-разработки, это, типа, самый крутой фреймворк, который все страшно любят? И что мне делать с админками, которые сейчас пишет четыре человека на AngularJS? Прийти и сказать: «Ребята, вы знаете, мы просто садимся и все переписываем. У нас ничего не станет лучше, мы просто переезжаем на новую версию». — «А зачем переезжаем?» — «Ну, потому что старую забросили». А они меня, конечно же, спросят: «Слушай, а есть такая штука, как в python, типа, «by 2 to 3». Т.е. давай натравим какой-то парсер, и он нам сейчас переформатирует код, и оно хоть как-то начнет собираться». Ты говоришь: «Извините, нет. Причем, мало того, что нет, оно сейчас еще все поменяется. Завтра у нас будет другой синтаксис». И это продать тоже никому невозможно. Совет директоров на меня посмотрит и уволит на завтра и все. Т.е. что делать с AngularJS — непонятно.

Javascript-фреймворки: должен остаться только один - 44

Мы дальше стали смотреть, и было второе решение — это React JS, который сделал Facebook. C удивлением оказалось, что это не фреймворк, а всего лишь UI библиотека. У нее очень четкая понятная структурность, четко понятно, что это единый data flow, который только получает данные, только их отрисовывает. Каждый компонент изолирован, у каждого компонента есть декларация, чего он получает на вход, чего он в итоге отрисовывает, чего внутри он хранит в виде промежуточного стейта. Нет, вообще, каких-то магических фильтров и прочей фигни, которую надо откуда-то подключать, учить и знать, как работает… В этом плане все максимально предсказуемо и это очень классно. Мне понравилось, что есть очень простая возможность тюнинга производительности — четко понятно, как она работает, четко понятно, как ее использовать, ничего суперсложного в этом нет. Есть даже серверный рендеринг, который нам не нужен, но круто, что он есть.

Javascript-фреймворки: должен остаться только один - 45

До кучи к этому шла архитектура Flux, которую активно двигает Facebook. Мы посмотрели на все это дело, у нас, как бы, был so-so. С одной стороны то, что one-way data flow, и то, что синхронная обработка как у стейт-машины, где четко понятно определенное состояние приложения — это супер. Но как приложение делится на независимые блоки и как разделить его, как в Angular JS, на какие-то отдельные сущности, не очень понятно. Потому что, мне кажется, там явно есть антипаттерны в виде того, что store’ы хранят данные, одновременно реализуют бизнес-логику, реализуют еще логику изменения этих данных. Т.е. это и модель, и контролер одновременно. Идея с единым диспетчером, как единый Event Bus с уникально именованными событиями, где можно быстро найти в коде, где что поменялось, делать быстрый рефакторинг, и компоненты работают изолировано друг от друга — это супер. Но совершенно непонятно, как обеспечивается динамика, как открыть, например, на одном экране два окна, которые бы работали независимо друг от друга, но при этом генерировали бы одинаково, например, события…

По большому счету, Facebook ни фига не зарелизил код, этого нет, это ни фига не фреймворк, это некая архитектурная идея. Причем, изобретенная в 80-х годах, которая называется Event Bus.

Javascript-фреймворки: должен остаться только один - 46

ОК. Давайте посмотрим какие-то сторонние фреймворки, которые реализуют эту фигню. Я пересмотрел штук пять, они все как две капли друг на друга похожи, и они все в этом плане намного хуже, чем AngularJS, и намного хуже, чем Ext JS, потому что в них нет почти ничего. Да, там лучше видно, как работает архитектура, но все еще нет речи ни о каком динамическом создании store’ов, их выгрузки и какой-то линковки UI компонентов между store’ами так, чтобы части приложения работали изолировано друг от друга и можно было бы два компонента создать одинаковыми, с одинаковым view, и два контроллера, и они бы не воевали друг с другом этими событиями. Поэтому в этом плане с разработкой веб-приложений все очень странно, т.е. зачастую непонятно, как это делать. Зато в этих фреймворках видно развитие javascript за последние 5 лет, в виде изоморфности, в виде npm-модулей, в виде вынесения функционала библиотеки, в виде поддержки ES6 — это хорошо. Только, блин, ни фига нет, вообще, никакой инфраструктуры тестирования у них, и непонятно как писать тест. Особенно с этой новой суперархитектурой непонятно, как писать тест. И нет интернационализации.

Попутно мы наткнулись на typescript’ы. В этом плане это, вообще, бомба.

Javascript-фреймворки: должен остаться только один - 47

Я подумал, что самый супер — убрать излишний креатив разных разработчиков, которые очень любят делать свои «велосипеды» — с помощью этого отлично можно. Typescript — это транс-компилятор, т.е. ты пишешь код, напоминающий javascript, с таким же синтаксисом, только расставляя типы. Т.е. у каждой переменной есть какой-то тип, причем, они могут быть сложными, может быть у переменной одновременно несколько типов, и в итоге он эти типы убирает, создавая тебе javascript-файл с абсолютно таким же синтаксисом, где просто убраны эти типы, и там еще есть несколько полезных syntactic sugarвещей, которые соответствуют стандартам ECMAScript 2006. Т.е. просто компилятор из ECMAScript 6 в 5.

Javascript-фреймворки: должен остаться только один - 48

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

Javascript-фреймворки: должен остаться только один - 49

Потом, там есть интерфейсы. Забегая вперед — фреймворк, который мы написали, основан сильно на интерфейсах, т.е. мы, ориентируясь на принцип solid, разбиваем на более мелкие зоны ответственности и делаем так, чтобы у нас разные классы зависели от небольших интерфейсов. Получается у нас больший декаплинг приложения.

Javascript-фреймворки: должен остаться только один - 50

Точно так же, как в python’е, есть поддержка дженериков. Есть декораторы, как в в python’е, есть дженерики, как это принято в мире С++, и которыми мы активно пользуемся. И есть составные типы. В редких случаях, можно сказать, что в этой переменной может быть или строка, или число, тогда, когда этого не избежать. Он тоже нормально с этим работает.

Javascript-фреймворки: должен остаться только один - 51

В итоге после этого исследования вопросов стало только больше. Какую архитектуру выбрать? Какое решение правильное? Как идти в ногу с развитием javascript, в виде ECMAScript 6? Где взять библиотеку UI компонентов?

И в этом плане Ext JS очень активно держит удар, в нем все это есть. В нем есть своя сборка, в нем есть механизм интернационализации, в нем есть понятная архитектура и прочее. Т.е. то, что получилось тогда, получилось странно, но только альтернативы то нет. Ну, как бы, она есть, но ее надо делать самому. В итоге, хотим фреймворк с Typescript, все хотели на этот момент фреймворк с Typescript. Без — уже не хотел никто.

Javascript-фреймворки: должен остаться только один - 52

Ну ладно, чего делать? Напишем задачу сами себе. JS-кодеры должны писать код. Верстальщики должны верстать и общаться с дизайнерами. Явно нужно сделать максимально простой UI слой, чтобы в нем разобрался дизайнер, и чтобы это было понятно программисту без жуткого наследования CSS-стилей в три-четыре уровня без жуткого наследования компонентов. Нужно сделать четкую и понятную архитектуру, код, примерно понятный бэкенд-разработчикам, и нужно разделение зон ответственности — отдельно верстка, отдельно код, отдельно контроллеры, отдельно хранение данных, стараться запихать состояние приложения в один какой-то тип классов, назвав его моделью. И, главное, больше границ, правил и стандартов: именования, форматирования кода, раскладки по файлам, всего этого дела. И при этом на Typescript. И при этом еще так, чтобы это все дело не было монолитным фреймворком, потому что все это дело нужно будет еще внедрить в существующий код.

Javascript-фреймворки: должен остаться только один - 53

Мы в итоге взяли фреймворк, который называется Este.js. Он ни о чем вам не говорит, он был наиболее революционно-инновационный. Плавно переписывали, пока от него не осталось вообще ничего.

В итоге было несколько проблем.

Javascript-фреймворки: должен остаться только один - 54

Первая проблема. Получается так, что store — это некий антипаттерн во flux. На практике у нас ничего хорошего с этим не вышло. Мы в итоге обратно разделили их на классы, которые хранят данные и могут их менять, и отдельно бизнес-логику в контроллере. Это примерно то же самое, куда сейчас идет развитие flux, когда у них есть отдельно вынесенные объекты со стейтом. У нас этот объект со стейтом просто называется store.

Javascript-фреймворки: должен остаться только один - 55

Было вот так.

Получилось примерно вот так:

Javascript-фреймворки: должен остаться только один - 56

Т.е. изолированные блоки, примерно так же, как в AngularJS, с таким же механизмом, в виде модулей. В каждом модуле там один или несколько своих store’ов, которые общаются с одним из нескольких контроллеров. И в итоге все это дело работает отдельно от view. View только читает данные store’ов и генерирует некие action’ы, которые единый dispatcher раскидывает (на самом деле, не единый, у каждого изолированного блока может быть еще свой) между контроллерами, каждый из которых подписывается на события и решает, реагирует он на это событие или нет. В итоге у нас приложение работает как набор вложенных друг в друга приложений, в каждом из которых внутри flux. Это иерархический MVC, где view просто вложены друг в друга, а контролеры изолированы.

Вторая проблема в том, что шаблоны Facebook предлагает делать вот так:

Javascript-фреймворки: должен остаться только один - 57

Мы, конечно, долго мучились, мы поняли, что это вообще не вариант, и мы стреляем себе в ногу. Я абсолютно не понимаю, зачем мне нужно смешивать кучу JS-кода, фигурных скобочек, круглых скобочек, логику и все вместе в UI компоненте, когда, по большому счету, суть UI компонента в том, что он берет какой-то шаблон и рендерит его в браузере. Мы посмотрели на такой проект как wix-react-templates, написали свой, сильно-сильно похожий. Но в нем нет кода. Только есть блоки, которые обеспечивают итерацию, if, then, else и переменные, больше ничего нет. Т.е. задача компонента — в эту переменную запихать заранее то, что в ней будет выведено. И задача компонента — указать сколько раз итерировать какой блок с какими данными. Так выглядит шаблон:

Javascript-фреймворки: должен остаться только один - 58

Вот такой мы написали свой собственный парсер этих шаблонов, который делает из него DOM-дерево и генерит такие шаблоны, сильно такие же, как их генерит JSХ:

Javascript-фреймворки: должен остаться только один - 59

Вот, это дело уже автоматически генерится, и с этим делом работает React.

Javascript-фреймворки: должен остаться только один - 60

И суть UI компонента в том, что он декларирует некие пропсы, декларирует некие стейты и в самом простом способе эти пропсы запихивает в блоки и в переменные. Построили некое дерево, которое потом рендерится.

Javascript-фреймворки: должен остаться только один - 61

Для переводов мы сделали штучку, которая выкусывает текст из этих шаблонов и генерит нам файл. Мы этот файл ручками переводим на русский язык. Можно туда добавить какую-то верстку. Обратите внимание, там в последней сточке добавлен span. Это дело мы пока закладываем для плюрализации слов.

Javascript-фреймворки: должен остаться только один - 62

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

Javascript-фреймворки: должен остаться только один - 63

С динамикой беда в том, что не очень Facebook объясняет, как это делается, как сделать независимой работу двух компонентов. Мы в этом плане нашли несколько вариантов решений и сейчас их прорабатываем.

Что в итоге получилось?

Javascript-фреймворки: должен остаться только один - 64

Получился фреймворк с хорошим ООП и с маленьким интерфейсом, с дженериками, близко похожий на то, как это было написано на java. С dependency injection, которая обеспечивает связь классов внутри одного модуля, у которого только две внешние зависимости — это React и lodash. В котором можно сделать любую архитектуру — захотим рефлакс, можно сделать рефлакс, захотим MVC — можно сделать MVC. Потому что там три с половиной файла и плюс React. И с нормальным механизмом сборки через web-pack…

Javascript-фреймворки: должен остаться только один - 65

Мы сделали два пилотных проекта — один на Ext JS 6, другой на React. Абсолютно с нуля, без использования существующего кода, оба повторяют один и тот же функционал существующей админки, но только одну единственную страницу на ней, на которой некоторые кнопки не кликаются. Достаточно немного, все это вместе заняло где-то месяц работы в сумме.

Задачи были — сделать анимации, сделать кастомный, нами рисуемый скроллбар, сделать отдельный интерфейс для узких экранов типа а-ля планшетов, сделать отдельно мобильную версию, когда экран совсем узкий, и сделать, чтобы все это авторизировалось и ходило в json rest, грузило реальные данные, которые у нас есть в API и с ними позволяло работать.

В итоге получились такие цифры:

Javascript-фреймворки: должен остаться только один - 66

На них можно не смотреть, но суть в том, что оба проекта реализованы с нуля, получились так, что flux + react выигрывал от 2-х до 5-ти раз. Но с большой звездочкой, что когда у нас будет столько же UI компонент, сколько есть в ExtJS, размеры билдов и скорость инициализации, она, конечно, упадет, потому что в этом плане мы тягаемся с монстром индустрии.

Javascript-фреймворки: должен остаться только один - 67

Ясно, что просто так на это дело не переехать. Поэтому мы сели, покурили все существующие интерфейсы с дизайнерами, собрали некий единый файл, в котором 60 простых компонентов типа панелей и кнопок, 15 сложных — это типа у нас есть таблица, в которой можно выключить какую-нибудь колонку, отсортировать ряды или, например, есть дроп-даун, в котором можно теги набирать — это прямо конкретно сложные вещи.

Javascript-фреймворки: должен остаться только один - 68

Я некоторые из них заверстал. Прямо сделал рабочими компонентами на react’е, это у меня заняло где-то 4 дня.

Javascript-фреймворки: должен остаться только один - 69

В итоге получилось так, что мы процентов 10 сделали, оценили, как 2 тыс. строк кода за 4 дня и получили примерную оценку в 2 человеко-месяца для реализации всех UI компонент, из которых состоят наши проекты.

Javascript-фреймворки: должен остаться только один - 70

Долго думали, как делать, но пока решили так, что новые проекты сразу делаем на новом фреймворке, попутно создавая эту UI библиотеку в отдельном репозитории. Старый код не трогаем, но встраиваем «независимыми блоками» в него новый функционал в виде как «берем отдельную панель или страницу, ее переписываем на новой». Либо, если это новый функционал, то, вообще, делаем эту страницу сызнова. Соответственно, новый фреймворк прикидывается UI компонентом внутри Ext JS, делая вид, что это какая-то панель, которая как-то отрисовывается и внутри себя чего-то делает, а наружу, максимум, одно-два события отдает. И, конечно же, при модификации старого кода ты либо портируешь существующий функционал, либо правишь, как есть.

Javascript-фреймворки: должен остаться только один - 71

Так что суть доклада, как всегда: превозмогайте трудности; велосипеды, баги, костыли — делайте все свое. Делайте свое, потому что ваша команда теперь обладает технологией и экспертными знаниями, как она работает. Т.е. мы читали исходники react’а и, наконец, понимаем, как же он, блин, работает.

Контакты

XEK
s@averin.ru
facebook
twitter
Блог компании Acronis

Этот доклад — расшифровка одного из лучших выступлений на конференции разработчиков высоконагруженных систем HighLoad++. Сейчас мы активно готовим конференцию 2016 года — в этом году HighLoad++ пройдёт в Сколково, 7 и 8 ноября.

Также некоторые из этих материалов используются нами в обучающем онлайн-курсе по разработке высоконагруженных систем HighLoad.Guide — это цепочка специально подобранных писем, статей, материалов, видео. Уже сейчас в нашем учебнике более 30 уникальных материалов. Подключайтесь!

Автор:

Источник


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


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