Веб-разработка, как и большинство технологий, движется волнами и циклами. Статические веб-сайты были всем, что у нас было в начале. Но довольно скоро благодаря Perl и PHP разработчики стали собирать первые серверные сайты. Это был прогресс, который в конечном итоге положил начало таким фреймворкам, как Laravel, Django или Rails.

Мобильные устройства изменят то, как люди используют Интернет. Итак, серверные веб-сайты, здравствуйте, клиентские приложения. Следующая волна принесла фреймворки, которые могли предоставить пользователям более похожий на приложение опыт — без перезагрузки — например, React или AngularJS.

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

Astro — яркий пример текущей волны, в которой мы находимся со времен Next.js, проведя нас по кругу: веб-фреймворк, который сочетает в себе серверный и клиентский рендеринг, чтобы получить лучшее из обоих миров.

Что такое Астро?

Astro — это фреймворк с открытым исходным кодом для создания веб-приложений поверх популярных UI-фреймворков, таких как React, Preact, Vue или Svelte. Страница Astro состоит из нескольких независимых компонентов. Чтобы сократить время загрузки, Astro удаляет весь JavaScript и выполняет предварительную визуализацию страниц на сервере, если разработчики не помечают компонент как интерактивный, и в этом случае Astro отправит минимальное количество JavaScript, необходимое для интерактивности.

Благодаря этой стратегии страницы Astro загружаются быстро, так как для первого рендеринга не требуется выполнять JavaScript. В процессе, называемом гидратацией, Astro «заливает» JavaScript в компоненты, чтобы сделать их динамичными.

Почему Астро?

Astro во многом отличается от других веб-фреймворков:

  • Скорость. Astro по возможности отображается статически и обрабатывается сервером, что делает его отличным выбором для веб-сайтов, оптимизированных для поисковой оптимизации, таких как блоги, обучающие программы или маркетинговые сайты.
  • Независимый от пользовательского интерфейса: в отличие от Next.js, который работает только с React, или Nuxt.js, для которого требуется опыт работы с Vue, Astro не навязывает вам какую-либо структуру пользовательского интерфейса. Вы можете не использовать ничего, свернуть свой собственный или использовать любую из множества поддерживаемых интеграций.
  • Темы. У Astro есть внушительный набор готовых тем и шаблонов, с помощью которых можно быстро приступить к работе.
  • Легко освоить: вам не нужно изучать React или Vue, чтобы начать работу с Astro. Его механизм шаблонов выглядит как обычный HTML, а код четко отделен от представления. Вы можете выбрать пустой проект и создать его в своем собственном темпе.
  • Батарейки в комплекте: Astro поставляется с функциями, которые мы ожидаем от современной платформы JavaScript/TypeScript, такими как ожидания верхнего уровня, поддержка Markdown и MDX, а также ESM.
  • Острова: островная архитектура позволяет нам смешивать статический контент, компоненты, отображаемые сервером и клиентом, на одной странице без конфликтов, что позволяет нам создавать интерактивные сайты с минимальными затратами. Мы даже можем смешивать разные фреймворки на одной странице, что дает нам еще один способ создания микрофронтендов. Чуть позже мы узнаем больше о том, как работают острова.

Начало работы с Астро

Чтобы начать работу с Astro, установите Node версии 16.12.0 или выше и выполните следующую команду. Следуйте указаниям мастера на экране и при появлении запроса выберите создание пустого проекта:

$ npm create astro@latest
 astro   v1.9.1 Launch sequence initiated.
✔ Where would you like to create your new project? … awesome-website
✔ How would you like to setup your new project? › an empty project
✔ Template copied!
✔ Would you like to install npm dependencies? (recommended) … yes
✔ Packages installed!
✔ Would you like to initialize a new git repository? (optional) … yes
✔ Git repository created!
✔ How would you like to setup TypeScript? › Relaxed
✔ TypeScript settings applied!
  next   Liftoff confirmed. Explore your project!

Затем вы можете запустить веб-сайт в режиме разработчика, войдя в каталог только что созданного и запущенного проекта: npm run dev и посетив http://localhost:3000.

Страницы и маршруты

Самое интересное в Astro происходит внутри папки src. Проверяя, что там, мы видим один каталог с именем pages с файлом index.astro.

Астро-страницы представляют собой смесь HTML, Javascript или TypeScript. Это значение по умолчанию index.astro:

---
---
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
        <meta name="viewport" content="width=device-width" />
        <meta name="generator" content={Astro.generator} />
        <title>Astro</title>
    </head>
    <body>
        <h1>Astro</h1>
    </body>
</html>

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

Под главной темой мы находим содержимое этого маршрута, расширенную HTML-форму, допускающую переменные. Мы можем, например, определить переменную во вступительной части и использовать ее в HTML следующим образом:

---
// src/pages/index.astro
const title = "Astro";
---
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
        <meta name="viewport" content="width=device-width" />
        <meta name="generator" content={Astro.generator} />
        <title>{title}</title>
    </head>
    <body>
        <h1>{title}</h1>
    </body>
</html>

Astro использует маршрутизацию на основе файлов, поэтому каждый файл в папке pages сопоставляется с маршрутом на веб-сайте. Например, если мы создадим файл с именем greetings.astro, мы должны увидеть его содержимое в http://localhost:3000/greetings.

---
const greeting = "Hello, world!";
---
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
        <meta name="viewport" content="width=device-width" />
        <meta name="generator" content={Astro.generator} />
        <title>Astro</title>
    </head>
    <body>
        <h1>{greeting}</h1>
    </body>
</html>

Помимо файлов .astro, Astro может анализировать файлы Markdown, MDX, JSX JavaScript и TypeScript. Например, если мы хотим написать сообщение в блоге в Markdown, мы создаем файл post.md в папке pages. Посещение маршрута заставит Astro конвертировать его в HTML на лету:

---
title: 'Learning Astro'
pubDate: 2023-01-10
description: 'A post written in Markdown.'ma
author: 'Tommy'
---
# Learning Astro
This Markdown file should be rendered as HTML when I visit http://localhost:3000/post

Компоненты

Компоненты Astro — это *.astro файлов с повторно используемым кодом и HTML. Мы можем использовать компоненты для написания таких элементов, как заголовки, нижние колонтитулы, панели навигации, кнопки и формы — все, что может быть выражено в виде HTML, может составлять компонент.

Давайте создадим наш первый компонент в src/components/Header.astro:

---
// src/components/Header.astro
---
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" 
<title>Astro</title>

После определения мы можем импортировать его на любую страницу (или в другие компоненты) и использовать следующим образом:

---
import Header from "../components/Header.astro";
---
<html lang="en">
    <head>
        <Header />
    </head>
    <body>
    </body>
</html>

Компоненты Astro ничем не отличаются от страниц. Любой код, определенный между заборами, выполняется на сервере. JavaScript удаляется перед отправкой содержимого в браузер.

Макеты

Макеты используются для уточнения повторно используемых структур пользовательского интерфейса. Технически они являются компонентами, поэтому синтаксис остается прежним.

Давайте заменим содержимое index.astro макетом:

---
// src/pages/index.astro
import SiteLayout from "../layouts/SiteLayout.astro";
---
<SiteLayout></SiteLayout>

Как видите, макеты по соглашению хранятся в папке src/layouts.

Макеты, как и компоненты, могут включать в себя другие компоненты. Здесь мы извлекли структуру из index.astro и добавили компонент Footer:

---
// src/layouts/SiteLayout.astro
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
---
<html lang="en">
    <head>
        <Header />
    </head>
    <body>
        <Footer />
    </body>
</html>

Реквизит и слоты

До сих пор наш сайт был полностью статичен. Чтобы передавать данные между страницами и компонентами, нам нужно понять, как работают props и slots.

Компоненты и макеты могут определять и принимать реквизиты (сокращение от свойств) через глобальный файл Astro.props. Значения, переданные через реквизиты, доступны компоненту перед рендерингом.

Мы можем прочитать реквизиты в нашем компоненте следующим образом:

---
// src/components/Header.astro
const { title } = Astro.props;
---
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" 
<title>{title}</title>

Значение title может быть указано при вызове компонента, что в следующем примере происходит через макет SiteLayout.

---
// src/layouts/SiteLayout.astro
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
---
<html lang="en">
    <head>
        <Header title = "Welcome my Astro Blog!" />
    </head>
    <body>
        <Footer />
    </body>
</html>

⚠️ Обратите внимание, что вам нужны пробелы вокруг знака равенства, т.е. title="Hello" НЕ ПРАВИЛЬНО. Вместо этого должно быть: title = "Hello".

Элементы Slot создают заполнители для содержимого, которое будет добавлено позже. Чтобы увидеть, как это работает, мы можем добавить элемент <slot /> в src/layouts/SiteLayout.astro:

---
// src/layouts/SiteLayout.astro
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
---
<html lang="en">
    <head>
        <Header title = "Welcome my Astro Blog!" />
    </head>
    <body>
        <slot />
        <Footer />
    </body>
</html>

Теперь HTML внутри <SiteLayout> вставляется в точку, где расположен слот.

---
// src/pages/index.astro
import SiteLayout from "../layouts/SiteLayout.astro";
---
<SiteLayout>
    <p>This content is rendered in the slot</p>
</SiteLayout>

Вложенные компоненты, макеты, реквизиты и слоты дают нам возможность создавать многократно используемые файлы пользовательского интерфейса на веб-сайте.

Острова и увлажнение

До этого момента мы не отправляли пользователю JavaScript; все предварительно визуализируется и используется как чистый HTML+CSS. Как мы можем отправить Astro отправить JavaScript в браузер? Для этого нам нужно понять архитектуру островов.

Шаблон островной архитектуры направлен на уменьшение объема JavaScript, необходимого на стороне браузера. Меньше JavaScript означает меньше отправляемых данных и меньшую вычислительную мощность, требуемую на устройстве пользователя. Остров — это автономный компонент, который объединяет HTML, CSS и — опционально — JavaScript. В шаблоне островов страница состоит из нескольких независимых островов.

Каждый остров предварительно визуализируется, поэтому интерактивность сразу после загрузки страницы отсутствует. Как только начальная страница готова, островки преобразуются в интерактивный контент в процессе, называемом гидратацией. Гидратация — это метод, который преобразует статический контент, доставленный через статический хостинг или рендеринг на стороне сервера, в динамическую страницу путем прикрепления обработчиков событий к элементам HTML.

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

Ниже приведен пример, показывающий, как Astro реализует острова. Давайте сначала добавим интеграцию пользовательского интерфейса в проект. Следующая команда устанавливает @astrojs/preact и preact.

$ npx astro add preact

Давайте создадим простую кнопку для тестирования интеграции:

// src/components/MyButton.jsx
export default function MyButton() {
const clicked = () => { console.log('Hello!') };
return (
    <div>
      <button style={{ color: 'purple' }} onClick={clicked}>Click me</button>
    </div>
  )
}

Как обычно, Astro попытается удалить любой JavaScript. Так что ничего не произойдет, если мы создадим экземпляр компонента с <MyButton />. Нам нужно сообщить Astro, что мы хотим, чтобы этот компонент рассматривался как остров и соответственно гидратировался, добавив template директиву client:load:

---
import MyButton from "../components/MyButton.jsx";
---
<html lang="en">
    <body>
        <MyButton client:load />
    </body>
</html>

При нажатии на кнопку должно быть напечатано «Hello!» в консоли браузера.

Директива клиента заставила Astro гидратировать компонент. Существует пять уровней гидратации с разными приоритетами:

  • client:load увлажняет компонент как можно быстрее.
  • client:idle увлажняет компонент, когда страница загружается. Полезно для низкоприоритетных компонентов, которым не требуется немедленная интерактивность.
  • client:visible={string} увлажняет компонент, как только он появляется в окне просмотра.
  • client:media={string} принимает запрос CSS в качестве аргумента и загружает компонент, как только он будет выполнен.
  • client:only полностью пропускает визуализацию HTML и отображает компонент в браузере.

Развертывание приложения Astro с CI/CD

Поскольку это Node-приложение, настроить сборку Astro с помощью CI/CD очень просто. Нам нужна всего пара работ.

Первое задание запускает npm ci для заполнения node_modules. Мы используем sem-version для выбора текущей версии Node.

checkout
sem-version node 19.4
npm ci
cache store

Второе задание запускает npm run build и сохраняет построенное приложение (находящееся в папке dist) как артефакт рабочего процесса.

checkout
sem-version node 19.4
cache restore
npm run build
artifact push workflow dist/

После того, как сайт построен, мы можем настроить непрерывное развертывание.

Цели развертывания

В зависимости от характера веб-приложения Astro можно развернуть как статический сайт а-ля Хьюго или Гэтсби, и в этом случае нам нужно только что-то простое, например, корзина S3 или страницы GitHub, или как полноценный серверный рендеринг. (SSR), где нам нужны конечные точки с поддержкой JavaScript или TypeScript.

Astro имеет встроенную поддержку различных популярных целей развертывания, таких как Netlify, Firebase, Vercel и Deno. Некоторые поддерживают только SSR или статический хостинг, в то время как другие могут делать и то, и другое.

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

Вот пример конвейера развертывания, нацеленного на статический сайт Netlify.

Для справки, рабочие команды следующие. Это предполагает, что мы уже получили токен API и сохранили его как секрет на семафоре с переменными env NETLIFY_TOKEN и NETLIFY_SITE.

checkout
artifact pull workflow dist
npm install -g netlify-cli
netlify deploy --dir=dist --prod --auth $NETLIFY_TOKEN --site $NETLIFY_SITE

Заключение

Популярность этого проекта была не чем иным, как астрономической: за первые пять месяцев с момента выпуска Astro 1.0 проект набрал более 25 000 звезд на GitHub. Успех Astro не случаен. Команда Astro создала веб-фреймворк, который предлагает отличную эргономику для разработчиков и быстро загружаемые веб-сайты для пользователей, даже если они используют маломощные устройства или медленное соединение.

Спасибо за прочтение и удачного строительства!

Первоначально опубликовано на https://semaphoreci.com 2 марта 2023 г.