Вышел Astro 7.4, который приносит флаг --timings для анализа времени сборки, разделение сервисов изображений для статической и динамической генерации, обработчик notFound() для пользовательских точек входа и предупреждения о неподдерживаемых настройках шрифтов. В экспериментальном режиме также доступны параллельный пререндеринг и кастомная страница редиректа для статических сайтов.
Анализ времени сборки с --timings
При медленной сборке не всегда очевидно, какой этап замедляет сборку: плагин Vite, подсветка синтаксиса, обработка изображений или рендеринг одной тяжёлой страницы. Флаг --timings в команде astro build решает эту проблему.
После завершения сборки Astro выводит сводку по фазам сборки, плагинам Vite, обработке Markdown, рендерингу страниц, изображениям и интеграциям.
Пример использования
На стартовом шаблоне блога добавили компонент <CommentSection>, который запрашивает комментарии, а каждый <Comment> — профиль автора:
---
const { comment } = Astro.props;
// Запрос профиля комментатора для каждого комментария
const res = await fetch(`https://jsonplaceholder.typicode.com/users?email=${comment.email}`);
const [user] = await res.json();
---
<article>
<strong>{user?.name ?? comment.name}</strong>
<p>{comment.body}</p>
</article>
На первый взгляд всё корректно, но запросы профилей не могут стартовать до получения комментариев, поэтому каждая страница ждёт два последовательных сетевых запроса. astro build --timings наглядно показывает узкое место:
build timings
total 2.61s · 9 pages · output "static"
Build phases
resolve config 59ms 2.3%
setup 225ms 8.6%
route discovery 2ms 0.1%
vite config 14ms 0.5%
content sync 206ms 7.9%
prerender bundle 151ms 5.8%
client bundle 21ms 0.8%
manifest injection 2ms 0.1%
generate pages 2.10s 80.6%
Page rendering (2.09s across 9 pages)
component render 2.08s 79.6%
page pipeline 13ms 0.5%
Slowest pages (median 297ms · p95 571ms)
/about 571ms
/blog/first-post 307ms /blog/[...slug]
/blog/using-mdx 303ms /blog/[...slug]
/blog/second-post 299ms /blog/[...slug]
/blog/third-post 297ms /blog/[...slug]
/blog/markdown-style-guide 296ms /blog/[...slug]
/rss.xml 10ms
/blog 6ms
Отчёт показывает, что 81% времени уходит на генерацию страниц, причём почти всё — на рендеринг компонентов. Самые медленные страницы используют макет поста блога, в то время как /blog и /rss.xml отрисовываются за миллисекунды. Это указывает на проблемный компонент.
Параллельная загрузка комментариев и профилей через Promise.all() в <CommentSection> и передача готовых профилей в <Comment> сократила генерацию страниц с 2,10 до 1,13 секунды и вдвое ускорила каждый пост.
Отчёт также охватывает плагины Vite, самые медленные файлы для сборки, плагины Markdown, подсветку синтаксиса и трансформации изображений, поэтому флаг полезен вне зависимости от типа задержек.
Раздельные сервисы изображений для сборки и рантайма
Предварительно отрендеренные (статичные) и динамические (on-demand) страницы предъявляют разные требования к обработке изображений. Во время сборки доступны полное окружение Node.js и достаточно времени, поэтому локальный оптимизатор вроде Sharp работает отлично. Динамические страницы выполняются на сервере или в edge-окружении, где Sharp часто недоступен, и там лучше подходит CDN изображений. Раньше приходилось выбирать один сервис для обоих сценариев либо полагаться на адаптер.
Astro 7.4 позволяет указать отдельные сервисы для build и runtime:
import { defineConfig } from 'astro/config';
export default defineConfig({
image: {
service: {
build: { entrypoint: 'astro/assets/services/sharp' },
runtime: { entrypoint: 'my-image-cdn-service' },
},
},
});
Сервис build обрабатывает изображения на статичных страницах и во время разработки, runtime — на динамических страницах и эндпоинте изображений в продакшене. Передача одного сервиса по-прежнему работает.
Переработана внутренняя логика отслеживания используемых изображений — теперь пререндереры участвуют в общем пайплайне Astro, а не строят его заново. Благодаря этому @astrojs/cloudflare обрабатывает изображения статичных страниц через тот же пайплайн, что и другие адаптеры.
Обработчик notFound() для кастомных точек входа
Композируемые обработчики из astro/fetch и astro/hono позволяют строить собственную серверную точку входа из компонентов Astro. Однако при использовании pages() без общего обработчика astro() запросы, не совпавшие ни с одним маршрутом, возвращали пустой 404 вместо заданной страницы 404.astro.
Astro 7.4 добавляет обработчик notFound(). Достаточно разместить его перед pages(), чтобы отрисовать свою страницу 404 для всех несовпадающих запросов:
import { Hono } from 'hono';
import { notFound, pages } from 'astro/hono';
import { cf } from '@astrojs/cloudflare/hono';
const app = new Hono();
app.use(cf());
app.use(notFound());
app.use(pages());
export default app;
Обработчик доступен и в astro/fetch. Поскольку astro() уже включает эту логику, notFound() нужен только при самостоятельной компоновке обработчиков.
Предупреждения о настройках шрифтов
Запросить несуществующее начертание или подмножество шрифта легко, а ошибка часто выявляется только по некорректному отображению текста. Теперь провайдеры шрифтов могут реализовать опциональный колбэк getFontProperties(), который сообщает реально доступные веса, стили, подмножества и форматы семейства. Astro предупреждает обо всех значениях в конфигурации, которые провайдер не может обслужить.
Все встроенные провайдеры шрифтов поддерживают этот механизм. Разработчикам пользовательских провайдеров рекомендуется внедрить getFontProperties() уже сейчас, так как в Astro 8 колбэк станет обязательным:
import type { FontProvider } from 'astro';
const provider: FontProvider = {
name: 'my-provider',
resolveFont: ({ familyName }) => {
// реализация
},
getFontProperties: ({ familyName }) => ({
weights: ['400', '700'],
styles: ['normal', 'italic'],
subsets: ['latin'],
formats: ['woff2'],
}),
};
Контекст init() для провайдеров теперь также включает функцию fetch() с повторными попытками при временных сбоях.
Обновление минимальной версии Node.js
Минимальная поддерживаемая версия Node.js для Astro и его официальных интеграций теперь — 22.22.0 (последний доступный релиз Node.js 22, поддерживаемый StackBlitz). Это соответствует политике поддержки Node.js в Astro. Перед обновлением Astro убедитесь, что у вас установлена Node.js 22.22.0 или новее.
Экспериментальный параллельный пререндеринг
Astro мог рендерить статичные страницы конкурентно, но всё происходило в одном потоке. На больших сайтах с ресурсоёмкими страницами сборка упиралась в одно ядро процессора. Флаг experimental.parallelPrerender распределяет рендеринг по воркерам Node.js:
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
parallelPrerender: true,
},
});
По умолчанию задействуются все доступные ядра, кроме одного. Если сборка начинает потреблять слишком много памяти или не ускоряется, задайте меньшее количество воркеров:
export default defineConfig({
experimental: {
parallelPrerender: {
workers: 4,
},
},
});
Каждый воркер одновременно рендерит столько страниц, сколько разрешено настройкой build.concurrency. Опция игнорируется, если адаптер предоставляет собственный механизм пререндеринга (среди официальных это только @astrojs/cloudflare); в таком случае Astro выведет предупреждение.
Флаг хорошо сочетается с инкрементальными статическими сборками: неизменённые страницы пропускаются, а требующие рендеринга распределяются по ядрам.
Экспериментальная кастомная страница редиректа
Статический сайт не может отправить HTTP-заголовок Location, поэтому Astro генерирует для каждого редиректа HTML-страницу с <meta http-equiv="refresh">. Это касается настроек redirects, вызовов Astro.redirect() на пререндеренных страницах и редиректов интернационализации. Разметка, текст и задержка такой страницы были фиксированными — нельзя было изменить оформление или перевод.
Флаг experimental.redirectPage позволяет задать собственную страницу:
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
redirectPage: true,
},
});
Затем создаётся src/pages/3xx.astro. Astro рендерит её для каждого редиректа, передавая пропсы from, to, status и delay:
---
import type { RedirectPageProps as Props } from 'astro';
const { from, to, status, delay } = Astro.props;
---
<html lang="en">
<head>
<meta http-equiv="refresh" content={`${delay};url=${to}`} />
<meta name="robots" content="noindex" />
<title>Redirecting…</title>
</head>
<body>
<p>This page has moved. <a href={to}>Continue to {to}</a>.</p>
</body>
</html>
В статической сборке именно <meta> выполняет редирект, поэтому страница должна его содержать. Если тег отсутствует, Astro предупреждает при сборке и использует встроенную страницу. Редиректы на динамических страницах работают без изменений.