Сравнение JSP и VueJS

Споры о том, насколько SPA лучше или хуже проверенных серверных технологий, не утихают до сих пор. Сторонники SPA (React, Angular, VueJS) в один голос говорят насколько это просто и удобно. Сторонники серверных технологий (PHP, JSP, ASP) ничего не говорят, потому что их мало и они заняты разработкой. Из-за того, что оба подхода достаточно разные, то и сравнивать их очень сложно. Зачастую сравнение сваливается во вкусовщину, так как нет людей, которые бы разрабатывали как с помощью одних, так и с помощью других. И даже, если такие уникумы находятся, никто в здравом уме не будет реализоваывать сложную функциональность одновременно и на той, и на другой технологии. Кроме меня.

TL;TR Я смог разработать такой компонент без каких-либо сложностей.

Начало

Так получилось, что r2server написан с помощью JSP, а r2cloud-ui с помощью VueJS. И вот мне понадобилось реализовать компонент по отображению OpenAPI в обоих проектах. В этот момент возникла уникальная ситуация:

  • Нужно реализовать абсолютно одинаковый компонент как в r2cloud-ui, так и в r2server.
  • У него должен быть один и тот же дизайн - bootstrap4. Несмотря на то, что приложения разные, я решил сделать максимально похожий user experience. Это удобно по нескольким причинам: во-первых, достаточно знать один набор компонент, во-вторых, пользователи, вполне очевидно, будут использовать и то, и другое приложение.

Требования

Прежде, чем начать сравнивать фреймворки, я хотел бы описать требования:

  • компонент должен быть не зависимым от приложения. Это прежде всего значит, что его можно встраивать не только в моё приложение, но и в любое другое.
  • он должен отображать описание OpenAPI с помощью bootstrap4. Это описание должно быть максимально похожим на стандартный swagger-ui.
  • (опционально) 80% покрытия тестами.

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

Простота старта

JSP vs VueJS

Для JSP начало работы достаточно простое:

  • создать стандартный maven проект
  • положить описание в /META-INF/tags

Однако, для VueJS старт очень запутанный. Я открыл официальный гид по созданию компонентов и увидел, что там рекомендуется начать с конфигурации rollup. Несмотря на то, что стандартом индустрии является webpack, я не нашёл упоминания о нём в документации. rollup действительно позволяет быстро начать разрабатывать компонент, но как только захочется посмотреть промежуточный результат, то тут ожидается облом. rollup просто компилирует файлы в разные форматы и ни о каком dev сервере речи нет.

"scripts": {
  "build": "npm run build:umd & npm run build:es & npm run build:unpkg",
  "build:umd": "rollup --config build/rollup.config.js --format umd --file dist/vue-openapi-bootstrap.umd.js",
  "build:es": "rollup --config build/rollup.config.js --format es --file dist/vue-openapi-bootstrap.esm.js",
  "build:unpkg": "rollup --config build/rollup.config.js --format iife --file dist/vue-openapi-bootstrap.min.js"
}

Простота упаковки

JSP vs VueJS

Тут о обоих фреймворков нет проблем. Для JSP упаковка - это стандартная команда maven:

mvn package

В VueJS соответственно:

npm build

Тестирование

JSP vs VueJS

Опять же, из-за rollup и общей путаницы с технологиями, начать тестировать компонент на VueJS нетривиально. Если честно, я так и не осилил переход на webpack с полноценными тестами и покрытием кода.

В JSP же всё относительно просто: я просто скопировал подход для тестирования тэгов, который использовал ранее и сразу получил страницу index.html, которая загружается в браузер простым кликом.

Скорость разработки

JSP vs VueJS

Да, инженерная мысль шагнула далеко вперед за последние 20 лет и разрабатывать на VuejS получается значительно быстрее. Например, синтаксис чуть более компактный. Сравните вывод одного и того же блока на VueJS:

<p>Available values:
{{ param.schema.items.enum.join(', ') }}
</p>

И на JSP:

<p>Available values:
<c:forEach items="${curParam.schema.items.getEnum()}" var="curEnum" varStatus="enumStatus"><c:if test="${!enumStatus.first}">, </c:if>${curEnum}</c:forEach>
</p>

Ещё одной вещью, значительно ускоряющей разработку (не путать со стабильностью кода!), является отсутствие строгой типизации. Сама по себе доменная модель OpenAPI нетривиальная, поэтому в JSP мне приходилось загружать её из JSON с помощью специальной библиотеки swagger-parser-v3. И даже после того, как я её загрузил, мне приходилось делать различные приседания, чтобы сгруппировать объекты в удобной виде:

private static Map<String, Operation> getOperationsByType(PathItem item) {
	Map<String, Operation> result = new HashMap<>();
	if (item.getGet() != null) {
		result.put("get", item.getGet());
	}
	...
	return result;
}

В javascript же такой проблемы не было, так как json - это JavaScript Object Notation. Что как бы намекает на глубокую поддержку внутри самого языка. Из-за этого загрузка выглядела достаточно просто:

openapi = response.data

Следующей неудобной штукой при разработке были вспомогательные методы. Например, необходимо было в зависимости от http метода отобразить цвет. В VueJS - это делается с помощью объявления метода прямо в файле:

methods: { 
  getColorByMethod (method) {
    ...
  }
}

И использование (всё в том же файле):

<span :class="`badge ${getColorByMethod(method)}`">

В JSP же пришлось объявлять специальные функциональные методы в jsp-openapi.tld:

<function>
	<name>getColorByMethod</name>
	<function-class>ru.r2cloud.openapi.OpenAPIHelper</function-class>
	<function-signature>java.lang.String getColorByMethod(ru.r2cloud.openapi.OperationDetails)</function-signature>
</function>

Описывать их в java коде:

public class OpenAPIHelper {
	public static String getColorByMethod(OperationDetails method) {
		...
	}
}

И использовать внутри JSP:

<span class="badge ${openapiHelper:getColorByMethod(method)}">

Простота переиспользования

JSP vs VueJS

Тут оба фреймворка постарались и сделали вполне неплохое переиспользование своих компонент. Подключить jsp tag в проект можно описав зависимость:

<dependency>
	<groupId>ru.r2cloud.openapi</groupId>
	<artifactId>jsp-openapi</artifactId>
	<version>1.0</version>
</dependency>

И добавив на страницу:

<%@ taglib prefix="openapi" uri="https://github.com/dernasherbrezon/jsp-openapi" %>
<openapi:bootstrap4-openapi openapi="${entity}"/>

Во VueJS примерно так же просто:

"dependencies": {
  "vue-openapi-bootstrap": "1.0.1"
}

Использование на странице:

<template>
	<vue-openapi-bootstrap :openapi="openapi"/>
</template>

<script>
import vueOpenapiBootstrap from 'vue-openapi-bootstrap/src/vue-openapi-bootstrap'
export default {
  components: {vueOpenapiBootstrap}
}
</script>

Выводы

Несмотря на то, что JSP уже почти 20 лет, разрабатывать переиспользуемые компоненты на нём достаточно просто. Конечно, чувствуется возраст технологии и некоторые вещи можно было бы сделать проще. Но тем не менее, ужасов, которые рисуют поклонники javascript и SPA, нет. При правильном дизайне и тот, и другой фреймворк предоставляют достаточно мощные инструменты для написания и переиспользования компонентов.