Современный Frontend: проблемы и пути решения. Пишем React-like приложение со строгой типизацией без сборщиков

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

Всем привет! Меня зовут Петр Солопов, я руководитель отдела фронтенд-разработки в SuperJob. Думаю, многие из вас видели популярную серию картинок в интернете про фронтенд и бэкенд: на бекенде всегда какой-то монстр, а на фронте — все мило, летают бабочки. На мой взгляд, это не соответствует действительности и все не так радужно и безоблачно: чего только стоят настройка Webpack, тона зависимостей, особенности фреймворков и многое другое. За подробностями под кат.

Что не так с React?

React де-факто стал стандартом при разработке интерфейсов в вебе. В этом можно убедиться, посмотрев на график скачиваний популярных фреймворков с npm:

График скачиваний frontend фреймворков
График скачиваний frontend фреймворков

Есть много статей, и вот одна из них, в которой говорится об основных недостатках React:

  1. Из коробки он не работает в браузере из-за JSX, т.к. это проприетарный синтаксис, невалидный для браузера. А писать, не используя JSX, на React практически невозможно.

  2. Для «Hello World» команда React предлагает инструмент create-react-app,  который загружает на вашу машину 2,5 миллиона строк кода зависимостей на JavaScript.

  3. Теперь React не просто библиотека для рендринга. В нем есть такие фичи, как server components, и другие.

Что не так с современным фронтендом?

Так как React — самый популярный фреймворк, то все его проблемы в той или иной степени специфичны для фронтенда в целом:

  1. Корпорации продвигают проприетарные вещи, а не развивают стандарты — «Hello World» на любом популярном фреймворке не работает в браузере;

  2. Колоссальное количество зависимостей в приложениях, сложности настройки сборки.

Об этих проблемах также упоминается в статьях (например, I don't want to do front-end anymore), которые становятся довольно популярными в сообществе.

It was easy to get started, too — you just created the files and refreshed the page.

Как это можно исправить

React и другие популярные фреймворки были выпущены в районе 2013 года. Тогда даже не было ES2015. Давайте посмотрим, какие на текущий момент есть инструменты, которые смогут решить вышеописанные проблемы. Первый инструмент, о котором хочется сказать, это нативные модули для JavaScript — ES-modules.

ES-modules

ES-модули приносят в JavaScript официальную унифицированную модульную систему. Однако, чтобы прийти к этому, потребовалось около 10 лет работы по стандартизации. Сегодня почти все основные браузеры из коробки поддерживают ES-модули, как и Node.js, начиная с 12-й версии.

Поддержка ES modules разными браузерами 
Поддержка ES modules разными браузерами 

ES-модули работают нативно в браузере у 93% пользователей. Поэтому их можно и нужно использовать в продакшене. Если вам нужно поддерживать браузеры, которые этого не умеют, например IE11, для них можно делать специальную сборку.

HTM

Следующий инструмент, который может помочь нам, это HTM. Удобная библиотека, с помощью которой можно писать JSX-like-синтаксис с помощью tagged templates, который будет работать прямо в браузере. HTM может работать как с React, так и Preact.

Unpkg

В примерах кода будет использован ресурс unpkg.com. Это просто CDN для npm. Его можно использовать для быстрой проверки гипотез, не скачивая себе на компьютер никаких зависимостей.

Hello world

Давайте с помощью всего вышеперечисленного напишем простейшее приложение, которое работает прямо в браузере. В примерах будет использован Preact, но это будет работать и с React’ом тоже. Создаем компонент и рендрим его на DOM-ноде:

<!-- index.html -->
<body>
  <div id="app"></div>
  <script type="module" src="main.js"></script>
</body>
// main.js
import {
  h,
  render
} from "https://unpkg.com/preact@10.5.13/dist/preact.module.js";
import htm from "https://unpkg.com/htm@3.0.4/dist/htm.module.js";

const html = htm.bind(h);

const App = ({ name }) => {
  return html`<div>Hello ${name}</div>`;
};

function renderApp() {
  const element = document.getElementById("app");
  render(html`<${App} name="world" />`, element);
}

renderApp();

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

Статическая типизация

Большие веб-приложения уже не пишутся на голом JavaScript. Используя статическую типизацию, например TypeScript или Flow, можно избежать многих ошибок и сделать код более поддерживаемый.

В примерах будем использовать TypeScript — это своеобразная надстройка над JS, которая позволяет писать типы в коде. Чтобы TypeScript работал в браузере, можно использовать специальный синтаксис и писать все типы в JSDocs. Получаем работающую статическую проверку кода, при этом код все еще работает прямо в браузере:

// main.js
import {
  html,
  render,
} from "https://unpkg.com/htm@3.0.2/preact/standalone.module.js";

/** @type {import('preact').FunctionalComponent<{name: string}>} */
const App = ({ name }) => {
  return html`<div>Hello ${name}</div>`;
};

function renderApp() {
  const element = document.getElementById("app");

  if (!element) throw new Error("element is not found");

  render(html`<${App} name="world" />`, element);
}

renderApp();

Управление импортами

С выходом Chrome 89 (и в Deno 1.8) мы получили нативное использование Import maps — механизма, который позволяет получить контроль над поведением JS-импортов.

Чем Import maps может быть полезен? К примеру, мы хотим использовать библиотеку «preact-router», которая внутри зависит от библиотеки «preact». Когда JS-интерпретатор дойдет до импорта «preact» в «preact-router», он упадет с ошибкой, так как не знает, что такое «preact» и откуда его брать. С помощью Import maps мы можем указать в html-файле, откуда брать зависимость, и JS-интерпретатор в рантайме успешно отработает.

<!-- index.html -->
<script type="importmap">
  {
    "imports": {
      "preact": "https://unpkg.com/preact@10.5.13/dist/preact.module.js",
      "htm": "https://unpkg.com/htm@3.0.4/dist/htm.module.js",
      "htm/preact": "https://unpkg.com/htm@3.0.4/preact/index.module.js",
      "preact-router": "https://unpkg.com/preact-router@3.2.1/dist/preact-router.es.js"
    }
  }
</script>
// main.js
import { html, render } from "htm/preact";
import { Router, Route } from "preact-router";


/** @type {import('preact').FunctionComponent<{ name: string }>} */
const Page = ({ name }) => {
  return html`<div>${name} page</div>`;
};

function App() {
  return html`
    <${Router}>
      <${Route} default component=${() => html`<${Page} name="Home" />`} />
      <${Route} path="/about" component=${() => html`<${Page} name="About" />`} />
    </Router>
  `;
}
function renderApp() {
  const element = document.getElementById("app");

  if (!element) throw new Error("element is not found");

  render(html`<${App} />`, element);
}

Подводные камни

У описанного выше подхода ограничение: зависимости должны быть ES-модулями. Но ввиду широкой поддержки ES-модулей, скорей всего в будущем, не собирать библиотеки под ES-модули будет моветоном.

Есть еще проблема: проверка типов не работает в tagged templates. Проблема решаемая, и уже есть инструменты, которое это умеют в lit (аналог htm), например lit-analyzer. Также есть issue в гитхаб в репозитории TypeScript, связанное с этим.

Заключение

В итоге удалось построить приложение, которое работает прямо в браузере. Вы можете самостоятельно посмотреть, как это работает, используя transpilation-free-starter-kit. В шаблоне также предусмотрена сборка для продакшена с поддержкой старых браузеров, установка зависимостей из npm и другое.

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

Используйте стандарты, пишите код, который исполняется в браузере, и все будет супер!

Источник: https://habr.com/ru/company/superjob/blog/583296/


Интересные статьи

Интересные статьи

Технология Backend-for-Frontend упрощает разработку сервисов, с которыми одновременно работают множество разных клиентов: компьютеры, смартфоны и планшеты со всеми возможными ОС.В прошлом...
Достаточно часто каждый аналитик сталкивается с ситуацией, когда загрузил данные в блок анализа, а в ответ – тишина, хотя в тестовом режиме все работает. Причина обычно в том, что данные ...
Привет, Хабр.В истории вычислительной техники существуют определенные события, повлиявшие на ход истории. Одним из таких моментов было появление первого массового персона...
Всем привет! Хочу поделиться своим опытом написания собственного терминала Linux используя Posix API. Усаживайтесь поудобнее.Что должен уметь наш терминал?1. Запуск проце...
Если в вашей компании хотя бы два сотрудника, отвечающих за работу со сделками в Битрикс24, рано или поздно возникает вопрос распределения лидов между ними.