Пропустить до содержимого

Скрипты и обработка событий

Вы можете добавить интерактивность в компоненты Astro без использования UI-фреймворка, такого как React, Svelte, Vue и т. д., используя стандартный HTML тег <script>. Это позволяет отправлять JavaScript в браузер и добавлять функциональность в компоненты Astro.

Скрипты могут использоваться для того, чтобы добавить обработчики событий, отправить аналитические данные, анимировать элементы и многого другого, что JavaScript может делать в Интернете.

src/components/ConfettiButton.astro
<button data-confetti-button>Celebrate!</button>
<script>
// Импорт модулей npm.
import confetti from 'canvas-confetti';
// Находим наш компонент DOM на странице.
const buttons = document.querySelectorAll('[data-confetti-button]');
// Добавляем обработчики событий, чтобы запускать конфетти при нажатии на кнопку.
buttons.forEach((button) => {
button.addEventListener('click', () => confetti());
});
</script>

По умолчанию Astro обрабатывает и собирает теги <script>, добавляя поддержку импорта модулей npm, написания TypeScript и т. д.

В файлы .astro можно добавить клиентский JavaScript, добавив один (или несколько) тегов <script>.

В этом примере добавление компонента <Hello /> на страницу приведет к выводу сообщения в консоль браузера.

src/components/Hello.astro
<h1>Welcome, world!</h1>
<script>
console.log('Welcome, browser console!');
</script>

По умолчанию теги <script> обрабатываются Astro.

  • Любые импорты будут скомпонованы, что позволит вам импортировать локальные файлы или модули Node.
  • Обработанный скрипт будет вставлен в <head> вашей страницы с помощью type="module".
  • TypeScript поддерживается полностью, включая импорт файлов TypeScript.
  • Если ваш компонент используется на странице несколько раз, скрипт будет добавлен в сборку и на страницу только единожды.
src/components/Example.astro
<script>
// Обработан! Собран! Поддерживает TypeScript!
// Импорт локальных скриптов и модулей Node работает.
</script>

Атрибут type="module" заставляет браузер воспринимать скрипт как модуль JavaScript. Это дает несколько преимуществ в плане производительности:

  • Рендеринг не блокируется. Браузер продолжает обрабатывать остальную часть HTML, пока загружается скрипт модуля и его зависимости.
  • Браузер ожидает обработки HTML перед выполнением модульных скриптов. Вам не нужно прослушивать событие “load”.
  • Атрибуты async и defer не нужны. Модульные скрипты всегда откладываются.

Чтобы запретить Astro обрабатывать скрипт, добавьте директиву is:inline.

src/components/InlineScript.astro
<script is:inline>
// Будет выведено в HTML именно так, как написано!
// Локальные импорты не разрешены и не будут работать.
// Если в компоненте, то повторяется каждый раз, когда компонент используется.
</script>
Подробнее о доступных директивах на тегах <script> смотрите на странице справочника директив.

Возможно, вы захотите написать свои скрипты в виде отдельных файлов .js/.ts или вам понадобится сослаться на внешний скрипт на другом сервере. Вы можете сделать это, сославшись на них в атрибуте <script> тега “src`.

Когда это использовать: когда ваш скрипт находится внутри src/.

Astro создаст, оптимизирует и добавит эти скрипты на страницу за вас, следуя своим правилам обработки скриптов.

src/components/LocalScripts.astro
<!-- относительный путь к скрипту в `src/scripts/local.js` -->
<script src="../scripts/local.js"></script>
<!-- также работает для локальных файлов TypeScript -->
<script src="./script-with-types.ts"></script>

Когда использовать: когда ваш JavaScript-файл находится внутри public/ или на CDN.

Чтобы загрузить скрипты вне папки src/ вашего проекта, включите директиву is:inline. Этот подход позволяет обойтись без обработки, сборки и оптимизации JavaScript, которые обеспечивает Astro, когда вы импортируете скрипты, как описано выше.

src/components/ExternalScripts.astro
<!-- абсолютный путь к скрипту по адресу `public/my-script.js` -->
<script is:inline src="/my-script.js"></script>
<!-- полный URL к скрипту на удаленном сервере -->
<script is:inline src="https://my-analytics.com/script.js"></script>

Некоторые UI-фреймворки используют собственный синтаксис для обработки событий, например onClick={...} (React/Preact) или @click="..." (Vue). Astro больше следует стандартному HTML и не использует пользовательский синтаксис для событий.

Вместо этого вы можете использовать addEventListener в теге <script> для обработки взаимодействия с пользователем.

src/components/AlertButton.astro
<button class="alert">Click me!</button>
<script>
// Находит все кнопки с классом `alert` на странице.
const buttons = document.querySelectorAll('button.alert');
// Обрабатывает клик на всех кнопках.
buttons.forEach((button) => {
button.addEventListener('click', () => {
alert('Button was clicked!');
});
});
</script>

Веб-компоненты с пользовательскими элементами

Заголовок раздела Веб-компоненты с пользовательскими элементами

Вы можете создавать собственные HTML-элементы с пользовательским поведением, используя стандарт Web Components. Определение пользовательского элемента в компоненте .astro позволяет создавать интерактивные компоненты без использования библиотеки UI-фреймворка.

В этом примере мы определяем новый HTML-элемент <astro-heart>, который отслеживает количество нажатий на кнопку сердца и обновляет <span> актуальным значением.

src/components/AstroHeart.astro
<!-- Оборачиваем компонент в наш пользовательский элемент "astro-heart". -->
<astro-heart>
<button aria-label="Heart">💜</button> × <span>0</span>
</astro-heart>
<script>
// Определяем поведение для нашего нового типа HTML-элемента.
class AstroHeart extends HTMLElement {
constructor() {
super();
let count = 0;
const heartButton = this.querySelector('button');
const countSpan = this.querySelector('span');
// При каждом нажатии на кнопку обновляем счетчик.
heartButton.addEventListener('click', () => {
count++;
countSpan.textContent = count.toString();
});
}
}
// Сообщаем браузеру, чтобы он использовал наш класс AstroHeart для элементов <astro-heart>.
customElements.define('astro-heart', AstroHeart);
</script>

Использование пользовательского элемента здесь имеет два преимущества:

  1. Вместо поиска по всей странице с помощью document.querySelector(), вы можете использовать this.querySelector(), который ищет только в пределах текущего экземпляра пользовательского элемента. Это позволяет упростить работу с дочерними элементами внутри элемента.

  2. Хотя <script> выполняется только один раз, браузер будет запускать метод constructor() нашего пользовательского элемента каждый раз, когда найдет на странице <astro-heart>. Это означает, что вы можете смело писать код для одного компонента, даже если собираетесь использовать этот компонент несколько раз на странице.

Подробнее о пользовательских элементах вы можете узнать из руководства по многоразовым веб-компонентам web.dev и введения в пользовательские элементы MDN.

В компонентах Astro код в frontmatter между кодовым забором --- выполняется на сервере и недоступен в браузере. Чтобы передавать переменные с сервера на клиент, нам нужен способ хранить наши переменные и затем считывать их при запуске JavaScript в браузере.

Один из способов сделать это - использовать атрибуты data-* для хранения значений переменных в HTML-выводах. Скрипты, включая пользовательские элементы, могут считывать эти атрибуты с помощью свойства dataset элемента, когда HTML загружается в браузере.

В этом примере компонента свойство message хранится в атрибуте data-message, поэтому пользовательский элемент может прочитать this.dataset.message и получить значение этого свойства в браузере.

src/components/AstroGreet.astro
---
const { message = 'Welcome, world!' } = Astro.props;
---
<!-- Храним реквизит сообщения как атрибут данных. -->
<astro-greet data-message={message}>
<button>Say hi!</button>
</astro-greet>
<script>
class AstroGreet extends HTMLElement {
constructor() {
super();
// Считываем сообщение из атрибута данных.
const message = this.dataset.message;
const button = this.querySelector('button');
button.addEventListener('click', () => {
alert(message);
});
}
}
customElements.define('astro-greet', AstroGreet);
</script>

Теперь мы можем использовать наш компонент несколько раз, и каждый раз нас будет приветствовать разное сообщение.

src/pages/example.astro
---
import AstroGreet from '../components/AstroGreet.astro';
---
<!-- Используйте сообщение по умолчанию: “Welcome, world!” -->
<AstroGreet />
<!-- Использовать пользовательские сообщения, передаваемые в качестве пропса. -->
<AstroGreet message="Lovely day to build components!" />
<AstroGreet message="Glad you made it! 👋" />