[Перевод] Использование 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 приложения, готовые к выпуску в продакшн.
Уважаемые читатели! Как вы создаёте прототипы веб-приложений?