Хабрахабр

[Перевод] Использование Fastify и Preact для быстрого прототипирования веб-приложений

Автор материала, перевод которого мы сегодня публикуем, хочет поделиться рассказом о том, какими технологиями он пользуется для быстрой разработки прототипов веб-приложений. В число этих технологий входят библиотеки Fastify и Preact. Он, кроме того, пользуется библиотекой htm. Она легко интегрируется с Preact и используется для описания элементов DOM с использованием понятных конструкций, напоминающих JSX. При этом для работы с ней не нужен транспилятор вроде Babel. Продемонстрировав инструментарий разработки прототипов и методику работы с ним, автор материала покажет как упаковывать такие приложения в контейнеры Docker. Это позволяет легко демонстрировать приложения всем, кому они интересны.

Начало

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

Я смог создать прототип очень быстро, коллеги получили возможность удобно экспериментировать с ним, они смогли оперативно высказать свои впечатления о нём. Мой эксперимент оказался крайне удачным. При этом испытать проект они могли даже в том случае, если на их компьютерах не были установлены Node.js и NPM.

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

Основные идеи

Если вы уже знакомы с Fastify и Preact и хотите узнать о том, как организовать разработку проектов на основе этих технологий, то вы находитесь буквально в паре шагов от желаемого. А именно, речь идёт о выполнении следующих команд:

git clone https://github.com/lmammino/fastify-preact-htm-boilerplate.git my-new-project
cd my-new-project
rm -rf .git
npm install

Конечно, вы можете поменять название проекта, my-new-project, на название вашего проекта.

А именно, речь идёт о следующем: После установки всего необходимого можно приступать к работе над проектом.

  • В папке src/ui собраны файлы клиентской части приложения (тут используются Preact и htm).
  • В папке src/server собраны файлы, относящиеся к серверной части приложения (тут используется Fastify).

Отредактировав соответствующие файлы, вы можете запустить проект:

npm start

После этого испытать его можно, перейдя в браузере по адресу localhost:3000.

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

Теперь давайте рассмотрим используемые здесь технологии и особенности работы с ними.

Fastify

Fastify — это быстрый и экономичный веб-фреймворк для Node.js. Этот проект изначально создали два программиста. Теперь же команда тех, кто над ним трудится, насчитывает 10 человек, более 130 человек помогают в разработке проекта, он собрал почти 10000 звёзд на GitHub.

Он изначально был нацелен на производительность, на удобство работы программистов и на расширение его возможностей с помощью плагинов. На Fastify оказали влияние Node.js-фреймворки, вроде Express и Hapi, которые существуют уже довольно давно. Это, кстати, одна из моих любимых особенностей Fastify.

Если вы не знакомы с фреймворком Fastify или хотите лучше его узнать, могу порекомендовать его официальную документацию.

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

Preact

Preact — это библиотека для разработки пользовательских интерфейсов для веб-проектов, которая была создана одним человеком как компактная и быстрая замена React. Этот проект оказался довольно успешным, им теперь занимается целая команда разработчиков, на GitHub он набрал более 20000 звёзд.

В обычных условиях этой библиотекой можно пользоваться с применением JSX в комбинации с Babel для транспиляции кода, но если вам не хочется устанавливать Babel и настраивать процесс сборки приложения, вы можете использовать Preact, например, совместно с библиотекой htm, которая использует шаблонные литералы и не требует транспиляции при запуске проектов, в которых она применяется, в современных браузерах. Одной из причин, по которой мне нравится Preact, является то, что у этой библиотеки есть расширяемый слой описания визуальных компонентов приложения.

Мы в этом материале будем пользоваться именно библиотекой htm и скоро рассмотрим несколько примеров.

Обзор проекта

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

Приложение в браузере

Это — одностраничное приложение (Single Page Application, SPA), в котором Preact и htm используются для формирования его клиентской части, а Fastify применяется для создания API, предназначенного для получения серверного времени.

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

Favicon

Настройка серверной части приложения

Начнём работу с создания новой папки:

mkdir server-time
cd server-time

Теперь инициализируем NPM-проект и установим Fastify:

npm init -y
npm i --save fastify@next fastify-static@next fastify-cli

Обратите внимание на то, что я, при описании некоторых пакетов-зависимостей, воспользовался конструкцией @next. Сделано это для того, чтобы в проекте использовалась бы библиотека Fastify 2, которая в настоящий момент находится в состоянии релиз-кандидата, но очень скоро станет основной стабильной версией.

Обратите внимание на то, что создать новый проект, основанный на Fastify, можно и с помощью инструмента командной строки fastify-cli:

npx fastify-cli generate server-time

Во время написания этого материала данная команда создаёт проект, рассчитанный на использование Fastify 1.x, но очень скоро, после релиза Fastify 2, это средство обновится.

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

  • fastify — это основной компонент фреймворка.
  • fastify-static — это дополнительный плагин, который позволяет удобно обслуживать статические файлы сервером Fastify.
  • fastify-cli — это средство командной строки, которое позволяет создавать проекты, основанные на Fastify.

В настоящий момент мы готовы к тому, чтобы создать API, основанное на Fastify. Поэтому давайте поместим серверный код в файл src/server/server.js:

const path = require('path') module.exports = async function(fastify, opts) ) // Сюда добавляют конечные точки API fastify.get('/api/time', async (request, reply) => { return { time: new Date().toISOString() } })
}

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

Fastify поддерживает и разработку в стиле async/await, и более традиционный подход, основанный на коллбэках. Первое, на что можно обратить внимание в этом коде, заключается в том, что тут используется ключевое слово async. Что именно выбрать — зависит от предпочтений конкретного разработчика.

Этот модуль (на жаргоне Fastify это называется «плагином») представляет собой функцию, которая принимает в качестве аргументов экземпляр Fastify (fastify) и набор опций (opts). Ещё одна интересная деталь заключается в том, что мы определяем здесь сервер как экспортируемый модуль. Именно это и происходит с плагином fastify-static. Внутри объявления модуля мы можем использовать экземпляр fastify для регистрации плагинов. Так же мы тут можем описывать конечные точки HTTP с использованием специальных методов, наподобие fastify.get и fastify.post.

Для начала надо отметить то, что он позволяет объединять несколько серверов. Применяемый здесь модульный подход, хотя и выглядит немного непривычно, имеет свои преимущества. Их можно легко встроить в существующее приложение, прикрепив их к путям наподобие /blog и /forum. Представьте себе, что вы создали сервер, предназначенный для обслуживания блога, и ещё один — для форума.

Более того, этот подход позволяет абстрагировать приложения и субприложения от привязок к серверу (речь идёт, например, о привязке сокетов), передавая решение этой задачи либо корневому приложению, либо fastify-cli.

Запустим сервер с использованием инструмента командной строки fastify:

node_modules/.bin/fastify start --log-level info src/server/server.js

Для того чтобы упростить себе жизнь, мы можем добавить эту команду в раздел scripts нашего файла package.json:

{ "scripts": { "start": "fastify start --log-level info src/server/server.js" }
}

Прежде чем действительно запускать сервер, нам нужно позаботиться о том, чтобы существовала папка, в которой будут расположены статические ресурсы. В противном случае fastify-static выдаст ошибку. Создадим эту папку:

mkdir src/ui

Теперь мы можем запустить приложение командой npm start и перейти с помощью браузера по адресу localhost:3000/api/time.

Если всё работает правильно, в браузере можно будет увидеть примерно следующее:

{ "time": "2019-02-17T19:32:03.354Z" }

В этот момент вы можете оценить ещё одну приятную возможность Fastify. Она заключается в том, что JSON-сериализация, в том случае, если некий маршрут возвращает объект, применяется автоматически.

Займёмся фронтендом. Теперь работа над серверным API завершена.

Настройка фронтенда

Весь код нашего проекта, имеющий отношение к фронтенду, будет находиться в папке src/ui. Он будет состоять из 5 файлов:

  • app.js — код Preact-приложения.
  • bootstrap.min.css — CSS-код для стилизации приложения (он взят прямо из фреймворка Bootstrap).
  • favicon.ico — favicon-файл. Если вы разрабатываете серьёзное приложение, без хорошего favicon-файла вам не обойтись.
  • index.html — главный HTML-файл нашего одностраничного приложения.
  • preacthtm.js — код библиотек Preact и htm.

Для начала поместим в папку файлы, представляющие собой стили, библиотеки и значок favicon:

curl "https://unpkg.com/htm@2.0.0/preact/standalone.js" > src/ui/preacthtm.js
curl "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" > src/ui/bootstrap.min.css
curl "https://github.com/lmammino/fastify-preact-htm-boilerplate/blob/master/src/ui/favicon.ico?raw=true" > src/ui/favicon.ico

Теперь создадим файл src/ui/index.html:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="/bootstrap.min.css" /> <title>My awesome server time</title> </head> <body> <div id="app"></div> <!-- JavaScript --> <script src="/preacthtm.js"></script> <script src="/app.js"></script> </body>
</html>

Перед нами вполне обычная HTML-страница, с помощью которой мы загружаем все ресурсы (CSS и JS) и создаём пустой элемент <div> с идентификатором app, в который мы выведем, во время выполнения проекта, наше приложение.

Теперь взглянем на код приложения, который должен находиться в файле src/ui/app.js:

/* глобальный htmPreact */
const { html, Component, render } = htmPreact class App extends Component { componentDidMount() { this.setState({ loading: true, time: null }) fetch('/api/time') .then(response => response.json()) .then(data => this.setState({ loading: false, time: data.time })) } render(props, state) { return html` <div class="container mt-5"> <div class="row justify-content-center"> <div class="col"> <h1>Hello from your new App</h1> <div> ${state.loading && html` <p>Loading time from server...</p> `} ${state.time && html` <p>Time from server: <i><font color="#999999">${state.time}</font></i>
</p> `} </div> <hr /> <div> Have fun changing the code from this boilerplate: <ul> <li>UI code available at <code>/src/ui</code></li> <li>Server-side code available at <code>/src/server</code></li> </ul> </div> </div> </div> </div> ` }
} render( html` <${App} /> `, document.getElementById('app')
)

В этом приложении есть лишь один компонент с состоянием, называемый App. Состояние этого компонента включает в себя 2 переменные:

  • loading — логическая переменная, используемая для указания на то, выполняется ли в некий момент времени запрос к серверному API для получения сведений о серверном времени.
  • time — строка, которая содержит последние полученные с сервера сведения о времени.

Если вы знакомы с React, то вы без труда поймёте вышеприведённый код.
С использованием Preact и htm мы можем создавать компоненты, объявляя классы, которые расширяют встроенный класс Component.

В этом классе мы можем описать поведение компонента, используя методы жизненного цикла, наподобие componentDidMount(), а также пользоваться методом, который ведёт себя как обычный метод render() из React.

В нашем случае, как только компонент будет прикреплён к странице (метод componentDidMount()), мы устанавливаем свойство loading состояния и выполняем запрос к API с использованием fetch.
После завершения запроса мы устанавливаем значение свойства состояния time и сбрасываем свойство loading в значение false.

В данном методе мы описываем DOM компонента с использованием htm. Метод render() вызывается автоматически при каждом изменении состояния компонента или при передаче ему новых свойств.

В пределах нашего шаблонного литерала могут присутствовать динамические выражения, наподобие тех, что мы используем для проверки состояния и для принятия решения о том, что вывести на экран в том случае, если приложение выполняет загрузку данных с сервера, и в том случае, если данные уже загружены. Библиотека htm позволяет описывать узлы DOM, используя тегированные шаблонные литералы со специальным тегом — html.

Делается это с помощью функции render() глобального объекта htmPreact. Ещё стоит отметить то, что нам нужно создать экземпляр приложения и вывести его на HTML-страницу.

Можете перезапустить сервер, перейти по адресу localhost:3000 и поэкспериментировать с тем, что мы только что создали. Теперь работа над фронтенд-приложением завершена. А когда то, что вы построите, покажется вам достаточно интересным для того, чтобы показать это кому-то ещё, вам, вероятно, полезно будет упаковать своё приложение в контейнер Docker. Например, можете разработать на базе этого приложения что-то своё.

Контейнеризация приложения

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

Благодаря Docker любой, кто попытается запустить у себя ваше приложение, будет избавлен от размышлений о том, установлена ли у него подходящая версия Node.js и NPM, ему не нужно будет, скачав исходники приложения, заботиться о том, чтобы, введя правильную последовательность команд, установить его зависимости и запустить сервер.

Для того чтобы упаковать приложение в контейнер Docker, нам нужно создать очень простой файл Dockerfile в корневой папке нашего проекта:

FROM node:11-alpine WORKDIR /app
COPY . /app
RUN npm install --production EXPOSE 3000 CMD ["npm", "start"]

Здесь мы описываем следующие действия:

  • Образ создаётся на основе образа Node.js 11, построенного на базе Alpine Linux.
  • Всё из текущей папки копируется в папку /app контейнера.
  • После этого мы выполняем команду npm install для загрузки и установки зависимостей. Использование флага --production приводит к тому, что установлены будут только зависимости, необходимые для развёртывания проекта в продакшне. Это ускоряет создание образа в том случае, если в проекте используется много зависимостей разработки.
  • Мы указываем на то, что у контейнера должен быть открыт пор 3000, на котором, по умолчанию, будет работать сервер.
  • В итоге мы описываем команду, npm start, которая будет выполнена во время запуска контейнера. Она запускает приложение.

Для того чтобы собрать образ для контейнера, выполним следующую команду:

docker build -t server-time .

Через несколько секунд образ должен быть готов и у вас должна быть возможность запустить контейнер:

docker run -it -p 3000:3000 server-time

Параметр -p позволяет настроить связь порта контейнера 3000 с локальным портом 3000. Это позволит обращаться к контейнеризированному приложению по адресу localhost:3000.
Теперь вы готовы к тому, чтобы поделиться своим приложением с другими людьми. Для того чтобы запустить его в среде Docker, достаточно, при условии, что на компьютере установлен Docker, выполнить в его папке две вышеприведённых команды.

Итоги

В этом материале мы рассказали о том, как создать среду для быстрой разработки веб-приложений с использованием Fastify и Preact. Кроме того, мы поговорили о том, как поделиться приложением с другими людьми, используя Docker.

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

  • Компиляция ресурсов интерфейсной части приложения: создание оптимизированных файлов (бандлов), возможно, средствами Webpack, Babel, или с привлечением других средств.
  • Маршрутизация в интерфейсной части приложения.
  • Серверный рендеринг.
  • Средства постоянного хранения данных.

Все эти возможности разработки реальных приложений ещё не добавлены в рассмотренный здесь набор технологий, поэтому я пока воспринимаю его как средство для разработки прототипов. Уверен, что если вам то, что вы увидели, понравилось, и вы рассматриваете всё это как основу будущих приложений, решающих реальные задачи, вы без особых сложностей сможете найти то, что вам нужно, и создавать на базе Fastify и Preact приложения, готовые к выпуску в продакшн.

Уважаемые читатели! Как вы создаёте прототипы веб-приложений?

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»