Сразу хочу оговориться, что данная статья не предназначена для тех, у кого разработка frontend основная деятельность. Данный пост предназначается для разработчиков backend которым понадобилось срочно прикрутить web UI или тех, кто просто интересуются новыми областями, а также fullstack разработчики.
Итак, перейдем к проблеме. Вспоминается статья, а также, на просторах Хабра было еще несколько подобных. Представлены они все как шуточные, но как говорится «в каждой шутке есть доля правды», а тут даже и не доля… Но вот в чем вопрос, насколько целесообразны все эти фреймворки, так ли они нужны?
Вопрос который я хотел бы поднять, преимущественно о сборке. Именно на этом этапе js-сообщество предлагает неимоверное количество инструментов, необходимость которых совсем не очевидна. Например, сообществом предлагается различные варианты работы с модулями, что имело смысл в свое время, но на данный момент спецификация на import/export поддерживается всеми современными браузерами, и даженедобраузером edge. Так как ранее мы уже упоминали, что для работы со старыми библиотеками у нас нет причин, рассмотрим на спецификации import/export. А вот то что действительно будет нам необходимо, так это какая либо работа с зависимостями.
Сделаю небольшое отступление, чтобы объяснить выбор технологий, которые будут использоваться в примере для самого frontend’а. Чтобы не копаться в бесчисленном многообразие фреймворков, пойдем от замечательного стандарта. Итак, все становится достаточно просто, виртуального DOM'а там нет, вебкомпоненты есть. На этом можно было бы закончить, но еще js. Если мы решим отделаться чистым js – то придется как-то решать инфраструктурные задачи, избавляться от бойлерплет кода и т.д. Иными словами – городить свой велосипед… Чтобы этого не делать, хотелось бы взять что-нибудь, где все уже сделано и достаточно качественно. Соответственно выбор мой пал на Polymer – фреймворк от Google, которые и пропихнули вебкомпоненты в стандарт.
Возвращаемся к сборке. Многие из вас (во всяком случае Java разработчики) привыкли к таким инструментом как maven и gradle, и наверняка успели выяснить, что они прекрасно справляются и с манипуляциями над ресурсами (у maven это конечно немного хуже, но вот у gradle — проблем нет собрать вообще все что угодно). Сообщество же frontend нам предлагает брать npm, для которого нужна nodeJs, но этого не хватит, еще и webpack сверху. А в них еще придется разбираться и ставить каждому разработчику. Эм… Хотелось бы просто выкачать репозиторий, сделать gradlew build и начать работать, так что давайте уж остановимся на привычном gradle.
Совсем без npm мы конечно обойтись не сможем, как минимум, нам нужен репозиторий откуда брать js библиотеки и npm это предоставляет, а вот утилита не обязательна. Можно найти несколько хороших решений вроде этого, но они всегда качают nodeJs, и запускают npm таски. Вариант, но мне бы хотелось бы минимизоровать общение с npm до взаимодействия с репозиторием и только. Как решение было написать свой плагин для gradle. Хоть и писать плагины для gradle достаточно просто, в силу широкого разнообразия возможностей указания версий для npm репозитория, задача несколько усложняется. Благо на это есть понятная документация.
Итак, напишем gradle скрипт для загрузки зависимостей. Используется kotlin dsl по той причине, что переписать на groovy dsl достаточно тривиально, а вот наоборот пришлось бы потратить время, если не было ранее опыта.
Данный плагин добавит нам таск npmLoad. Чтобы позволить IDE красиво сгрупировать, определим его в группу
Отлично, можно попробовать написать код для нашего frontend. Встроенный сервер Intellij по умолчанию скажет вам что Method Not Allowed на попытку подключить скрипты через import. Чтобы это избежать – поставьте чекбокс allow unsigned requests. (в последних версиях работает и без этого).
Попробуем теперь запустить, и… Все равно ничего не заработало. Отвалились зависимости внутри компонентов полимера. Дело в том, что многие js библиотеки пушут импорты с расчетом на какой-нибудь транспилер вроде babel. Такой import выглядит например так:
Такой импорт по синтаксису не верный, так как для браузера путь должен начинаться только с ‘/’, ‘./’ или ‘../’. Кроме того, в таком виде его нельзя брать ни как относительный, ни как абсолютный, так как здесь идет расчет на начало из корня директории с библиотеками. Следовательно такие пути надо исправить, ну и чтобы не делать это самим – можно взять заготовленный плагин.
В зависимости
Добавится task jsImportFix который приведет все импорты в порядок.
Вот так просто, и без необходимости разбираться с горой новый инструментов, можно собрать фронт. Но давайте еще рассмотрим вопрос со стилями. Откровенно говоря, веб-компоненты позволяют избавится от бойлерплейта, а переменные в css, которые уже в стандарте – открывают множество возможностей, так что необходимости в таких вещах как sass уже больше нет. Но вдруг, например вам очень приглянулся bootstrap и захотелось сделать дизайн на его основе.
Найти какой-нибудь плагин для сборки на этот счет не проблема, в основном, все они за основу берут libsass и есть обертка на java — jsass. Единственная проблема у всех (во всяком случае на момент когда я рассматривал их) в дуплицирование зависимых файлов.
Пример:
Файл a:
Файлы _b и _c:
В результате мы получим в основном файле 2 одинаковых блока из файла _c. Можно достаточно просто исправить, если дописать импортер к jsass, api позволяет. Собственно поэтому я опять со своим решением и здесь (если подобных требований нет, то лучше воспользоваться другим решением).
Добавим в список npm зависимостей
Зависимость на плагин
Фейковый таск processResources придется создать, если не подключён java-plugin (а он нам здесь и не нужен). Это конечно недоработка, потом обязательно исправлю.
Обратите внимание на то, что полученный css файл нужно подключать не в head, так как внутри компонента он тогда не будет виден, а непосредственно в шаблон самого компонента.
Последним шагом можно немного изменить скрипт, чтобы получить привычную директорию build, с готовым проектом что можно деплоить на прод.
Полный код выложил на github (наверно придется потом мигрировать на gitlab...).
Надеюсь, основная идея понятна, а дальше можно добавлять достаточно просто абсолютно все что угодно. Спасибо за прочтение.
Итак, перейдем к проблеме. Вспоминается статья, а также, на просторах Хабра было еще несколько подобных. Представлены они все как шуточные, но как говорится «в каждой шутке есть доля правды», а тут даже и не доля… Но вот в чем вопрос, насколько целесообразны все эти фреймворки, так ли они нужны?
Вопрос который я хотел бы поднять, преимущественно о сборке. Именно на этом этапе js-сообщество предлагает неимоверное количество инструментов, необходимость которых совсем не очевидна. Например, сообществом предлагается различные варианты работы с модулями, что имело смысл в свое время, но на данный момент спецификация на import/export поддерживается всеми современными браузерами, и даже
Сделаю небольшое отступление, чтобы объяснить выбор технологий, которые будут использоваться в примере для самого frontend’а. Чтобы не копаться в бесчисленном многообразие фреймворков, пойдем от замечательного стандарта. Итак, все становится достаточно просто, виртуального DOM'а там нет, вебкомпоненты есть. На этом можно было бы закончить, но еще js. Если мы решим отделаться чистым js – то придется как-то решать инфраструктурные задачи, избавляться от бойлерплет кода и т.д. Иными словами – городить свой велосипед… Чтобы этого не делать, хотелось бы взять что-нибудь, где все уже сделано и достаточно качественно. Соответственно выбор мой пал на Polymer – фреймворк от Google, которые и пропихнули вебкомпоненты в стандарт.
Возвращаемся к сборке. Многие из вас (во всяком случае Java разработчики) привыкли к таким инструментом как maven и gradle, и наверняка успели выяснить, что они прекрасно справляются и с манипуляциями над ресурсами (у maven это конечно немного хуже, но вот у gradle — проблем нет собрать вообще все что угодно). Сообщество же frontend нам предлагает брать npm, для которого нужна nodeJs, но этого не хватит, еще и webpack сверху. А в них еще придется разбираться и ставить каждому разработчику. Эм… Хотелось бы просто выкачать репозиторий, сделать gradlew build и начать работать, так что давайте уж остановимся на привычном gradle.
Совсем без npm мы конечно обойтись не сможем, как минимум, нам нужен репозиторий откуда брать js библиотеки и npm это предоставляет, а вот утилита не обязательна. Можно найти несколько хороших решений вроде этого, но они всегда качают nodeJs, и запускают npm таски. Вариант, но мне бы хотелось бы минимизоровать общение с npm до взаимодействия с репозиторием и только. Как решение было написать свой плагин для gradle. Хоть и писать плагины для gradle достаточно просто, в силу широкого разнообразия возможностей указания версий для npm репозитория, задача несколько усложняется. Благо на это есть понятная документация.
Итак, напишем gradle скрипт для загрузки зависимостей. Используется kotlin dsl по той причине, что переписать на groovy dsl достаточно тривиально, а вот наоборот пришлось бы потратить время, если не было ранее опыта.
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath("com.github.artfable.gradle:gradle-npm-repository-plugin:0.0.3")
}
apply(plugin = "artfable.npm")
configure<GradleNpmRepositoryExtension> {
output = "$projectDir/src/libs/"
dependencies = mapOf(
Pair("@polymer/polymer", "3.0.5"),
Pair("@polymer/app-layout", "3.0.1"),
Pair("@polymer/paper-toolbar", "3.0.1"),
Pair("@polymer/paper-icon-button", "3.0.1"),
Pair("@polymer/iron-icons", "3.0.1"),
Pair("@webcomponents/webcomponentsjs", "2.1.3")
)
}
Данный плагин добавит нам таск npmLoad. Чтобы позволить IDE красиво сгрупировать, определим его в группу
tasks["npmLoad"].group = "frontend"
Отлично, можно попробовать написать код для нашего frontend. Встроенный сервер Intellij по умолчанию скажет вам что Method Not Allowed на попытку подключить скрипты через import. Чтобы это избежать – поставьте чекбокс allow unsigned requests. (в последних версиях работает и без этого).
Попробуем теперь запустить, и… Все равно ничего не заработало. Отвалились зависимости внутри компонентов полимера. Дело в том, что многие js библиотеки пушут импорты с расчетом на какой-нибудь транспилер вроде babel. Такой import выглядит например так:
import '@polymer/polymer/polymer-legacy.js';
Такой импорт по синтаксису не верный, так как для браузера путь должен начинаться только с ‘/’, ‘./’ или ‘../’. Кроме того, в таком виде его нельзя брать ни как относительный, ни как абсолютный, так как здесь идет расчет на начало из корня директории с библиотеками. Следовательно такие пути надо исправить, ну и чтобы не делать это самим – можно взять заготовленный плагин.
В зависимости
classpath("com.github.artfable.gradle:gradle-js-import-fix-plugin:0.0.1")
apply(plugin = "artfable.js.import.fix")
configure<GradleJsImportFixExtension> {
directory = "$projectDir/src/libs"
}
Добавится task jsImportFix который приведет все импорты в порядок.
Вот так просто, и без необходимости разбираться с горой новый инструментов, можно собрать фронт. Но давайте еще рассмотрим вопрос со стилями. Откровенно говоря, веб-компоненты позволяют избавится от бойлерплейта, а переменные в css, которые уже в стандарте – открывают множество возможностей, так что необходимости в таких вещах как sass уже больше нет. Но вдруг, например вам очень приглянулся bootstrap и захотелось сделать дизайн на его основе.
Найти какой-нибудь плагин для сборки на этот счет не проблема, в основном, все они за основу берут libsass и есть обертка на java — jsass. Единственная проблема у всех (во всяком случае на момент когда я рассматривал их) в дуплицирование зависимых файлов.
Пример:
Файл a:
@import “b”;
@import “c”;
Файлы _b и _c:
@import “d”;
В результате мы получим в основном файле 2 одинаковых блока из файла _c. Можно достаточно просто исправить, если дописать импортер к jsass, api позволяет. Собственно поэтому я опять со своим решением и здесь (если подобных требований нет, то лучше воспользоваться другим решением).
Добавим в список npm зависимостей
Pair("bootstrap", "4.1.3")
Зависимость на плагин
classpath("com.github.artfable.gradle:gradle-sass-plugin:0.0.1")
apply(plugin = "artfable.sass")
configure<GradleLibsassPluginExtension> {
group(delegateClosureOf<GradleLibsassPluginGroup> {
sourceDir = "src/sass"
outputDir = "src/css"
} as Closure<Any>)
}
tasks.create("processResources")
Фейковый таск processResources придется создать, если не подключён java-plugin (а он нам здесь и не нужен). Это конечно недоработка, потом обязательно исправлю.
Обратите внимание на то, что полученный css файл нужно подключать не в head, так как внутри компонента он тогда не будет виден, а непосредственно в шаблон самого компонента.
Последним шагом можно немного изменить скрипт, чтобы получить привычную директорию build, с готовым проектом что можно деплоить на прод.
Полный код выложил на github (наверно придется потом мигрировать на gitlab...).
Надеюсь, основная идея понятна, а дальше можно добавлять достаточно просто абсолютно все что угодно. Спасибо за прочтение.