В настоящее время мы все привыкли видеть появление новых фреймворков, которые обещают произвести революцию в сфере веб‑разработки. Тем не менее, чаще всего мы остаемся привязанными к конкретной библиотеке пользовательского интерфейса (React, Vue, Svelte и т. д.) для определения наших компонентов и создания пользовательского опыта.

На этот раз ситуация поменялась! Используя возможности Vite.js, мы получили Astro: агностический фреймворк, который может работать как SSG (генератор статических сайтов) и обеспечивать SSR (рендеринг на стороне сервера).

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

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

Астро 101: краткое введение

Если вы посмотрите определение на официальном сайте компании, то обнаружите следующее:

Astro — это универсальный (все‑в-одном) веб‑фреймворк для создания быстрых, ориентированных на контент веб‑сайтов.

Недавно они выпустили первый стабильный релиз, обозначив фреймворк как готовый к продакшну, что является важной вехой для новых фреймворков. Веб‑сообщество отреагировало очень позитивно, и с каждым днем мы видим все больше проектов, поставляемых с фронтендами Astro.

Astro обладает множеством замечательных возможностей для удобства разработчиков и пользователей. Я рекомендую ознакомиться с этой статьей LogRocket для более общего обзора Astro.

По моему личному мнению, ключевым отличием Astro от других фреймворков является его островная архитектура. Эта концепция была впервые описана в 2019 году Кэти Сайлор‑Миллер (Katie Sylor‑Miller) и расширена позже в этом посте создателем Preact Джейсоном Миллером (Jason Miller).

Что такое Astro острова?

Термин «Astro остров» относится к интерактивному компоненту пользовательского интерфейса на статичной HTML‑странице. На странице может существовать несколько островов, и каждый из них всегда отображается изолированно. Это означает, что каждый остров может использовать любой фреймворк пользовательского интерфейса или даже обычный код Astro, наряду с другими островами на странице.

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

Возьмем в качестве примера имплементацию компонента Counter, выполненного в React. При первом рендеринге он покажет кнопку с текстом «Counter: 0», и каждый раз, когда пользователь кликает на нее, счетчик будет увеличиваться на 1.

// src/components/Counter.tsx
import { useState } from 'react';

const Counter = () => {
  const [count, setCounter] = useState(0);
  return (
    <button onClick={() => setCounter((number) => number + 1)}>
      Counter: {count}
    </button>
  );
};

export default Counter;

Затем выполним рендеринг этого компонента в Astro.

Чтобы использовать компоненты React в вашем Astro-проекте, вы должны добавить в свой проект интеграцию @astrojs/react.

// src/pages/index.astro
---
import Counter from '../components/Counter';
---

<Counter />

Этот код отобразит кнопку, но поскольку JavaScript во время сборки будет удален с помощью Astro, пользователь не сможет инкрементировать счетчик. Когда мы хотим сделать наше приложение интерактивным, следует действовать явно, используя клиентские директивы (рассмотрим их позже).

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

// src/pages/index.astro
---
import Counter from '../components/Counter.jsx';
--

<Counter client:load />

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

Чем Astro острова отличаются от остальных?

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

Отличным примером является is-land (от команды 11ty), который обеспечивает дополнительные условия при гидратации компонента (они же клиентские директивы), такие как:

  • on:interaction

  • on:save-data

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

<is-land on:interaction>
  <form>
    <button type="button">Hydrate the island</button>
  </form>

  <p>This content is before hydration.</p>

  <template data-island="replace">
    <vanilla-web-component>My component content after hydration</vanilla-web-component>
  </template>
</is-land>

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

Если вы хотите узнать больше об архитектуре островов, я нашел на GitHub awesome-islands как отличный ресурс, который организует много контента, связанного с архитектурой островов.

Все директивы клиента Astro

Директива — это атрибут компонента, который указывает Astro, как он [компонент] должен быть отображен. На момент написания этой статьи Astro поддерживает в общей сложности пять различных клиентских директив. Это число может измениться по мере добавления новых возможностей.

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

  • <MyComponent client:load/>: Гидратирует JavaScript компонент сразу после загрузки страницы

  • <MyComponent client:idle/>: Гидратирует JavaScript компонент после завершения начальной загрузки страницы и возникновения события requestIdleCallback

  • <MyComponent client:visible/>: Гидратирует JavaScript компонент, как только компонент попадает в область просмотра пользователя. Здесь используется внутренний IntersectionObserver для отслеживания видимости

  • <MyComponent client:media={string}/>: Гидратирует JavaScript компонент при выполнении определенного медиа-запроса CSS

  • <MyComponent client:only={string}/>: Пропускает серверный рендеринг HTML и осуществляет рендеринг только на клиенте. Компонент будет пропущен во время сборки, что делает его полезным для компонентов, которые полностью зависят от API на стороне клиента.

Работа с клиентскими директивами

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

Ниже приведен весь код страницы, изображенной на скриншоте выше. Содержимое страницы представляет собой один и тот же компонент Counter, который рендерится в общей сложности шесть раз: сначала без указания какой-либо клиентской директивы (компонент вообще не интерактивен), а потом остальное с использованием всех различных клиентских директив, которые мы рассмотрели выше.

// src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
import Counter from '../components/Counter';
---

<Layout title="Welcome to Astro">
  <main>
    <h1>Welcome to <span class="text-gradient">Astro</span></h1>

    <h2><pre>no directive</pre></h2>
    <p class="instructions">
      <code>No JS, no interactive</code>
      <Counter />
    </p>

    <h2><pre>client:load</pre></h2>
    <p class="instructions">
      <code>Loads JS as soon as possible</code>
      <Counter client:load />
    </p>

    <h2><pre>client:idle</pre></h2>
    <p class="instructions">
      <code>Loads JS when rendering is over</code>
      <Counter client:idle />
    </p>

    <h2><pre>client:visible</pre></h2>
    <p class="instructions">
      <code>Loads JS when the button is visible to the user</code>
      <Counter client:visible />
    </p>

    <h2><pre>client:media</pre></h2>
    <p class="instructions">
      <code>Loads JS when the media query (min-width: 680px) is valid</code>
      <Counter client:media="(min-width: 680px)" />
    </p>

    <h2><pre>client:only</pre></h2>
    <p class="instructions">
      <code>Loads JS only in client (No SSR)</code>
      <Counter client:only="react" />
    </p>
  </main>
</Layout>

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

Заключительные слова

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

Еще одним большим преимуществом Astro является то, что он не зависит от пользовательского интерфейса. Это означает, что вы можете использовать свою собственную структуру пользовательского интерфейса (BYOF)! React, Preact, Solid, Svelte, Vue и Lit официально поддерживаются в Astro. Вы даже можете смешивать и сочетать различные фреймворки на одной странице, что облегчает будущую миграцию и предотвращает привязку проекта к одному фреймворку.


Приглашаем всех желающих на открытое занятие, на котором рассмотрим хорошие практики обработки форм со сложным состоянием. Записаться можно на странице онлайн-курса "React.js Developer".

Комментарии (0)