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

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

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

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

src/components/ConfettiButton.astro
<button data-confetti-button>Поздравляем!</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> теги, добавляя поддержку импортов, TypeScript’а и другие функции.

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

В примере ниже добавляется <Hello /> компонент, который залогирует сообщение в браузерную консоль.

src/components/Hello.astro
<h1>Привет, Astro!</h1>
<script>
console.log('Добро пожаловать в браузерную консоль!');
</script>

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

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

Для того, чтобы избежать сборки скрипта, вы можете добавить директиву is:inline.

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

📚 Обратитесь к нашему описанию директив за более подробной информацией о доступных директивах для тегов <script>.

Вам может потребоваться описать свой скрипт в отдельном .js/.ts файле или подключить внешний скрипт с другого сервера. Этого можно достичь, добавив путь до файла в атрибут src тега <script>.

Когда использовать: Если ваш скрипт расположен в папке 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">Кликни меня!</button>
<script>
// Находит все кнопки с классом `alert` на странице.
const buttons = document.querySelectorAll('button.alert');
// Обрабатывает клик на всех кнопках.
buttons.forEach((button) => {
button.addEventListener('click', () => {
alert('Кнопка была нажата!');
});
});
</script>

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

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

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

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

src/components/AstroHeart.astro
<!-- Оборачиваем компонент в свой элемент “astro-heart”. -->
<astro-heart>
<button aria-label="Сердце">💜</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;
});
}
}
// Оповещаем браузер, о том, что существует реализация для `astro-heart` тега.
customElements.define('astro-heart', AstroHeart);
</script>

Есть 2 преимущества использования кастомных элементов здесь:

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

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

📚 Вы можете получить больше информации о кастомных элементах на странице гайда web.dev’s Reusable Web Components и MDN’s Введение в кастомные компоненты.

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

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

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

src/components/AstroGreet.astro
---
const { message = 'Привет, Astro!' } = Astro.props;
---
<!-- Сохранить свойство message как data атрибут. -->
<astro-greet data-message={message}>
<button>Сказать "Привет!"</button>
</astro-greet>
<script>
class AstroGreet extends HTMLElement {
constructor() {
super();
// Прочитаем message свойство из data атрибута.
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';
---
<!-- Сообщение по умолчанию: Привет, Astro!” -->
<AstroGreet />
<!-- Use custom messages passed as a props. -->
<AstroGreet message="Подходящий день для разработки компонент!" />
<AstroGreet message="Вы это сделали! 👋" />