Режимы рендеринга
Nuxt поддерживает разные режимы рендеринга: универсальный рендеринг, рендеринг на клиенте, а также предоставляет гибридный рендеринг и возможность рендерить ваше приложение на пограничных CDN-серверах.
И браузер, и сервер могут интерпретировать код JavaScript для преобразования компонентов Vue.js в HTML-элементы. Этот шаг называется рендеринг. Nuxt поддерживает как универсальный, так и клиентский рендеринги. У этих двух подходов есть преимущества и недостатки, которые мы рассмотрим.
По умолчанию Nuxt использует универсальный рендеринг, чтобы обеспечить лучший пользовательский опыт, производительность и оптимизировать индексацию поисковыми системами, но вы можете переключать режимы рендеринга с помощью одной строки конфигурации.
Универсальный рендеринг {#universal-rendering}
Этот шаг похож на классический рендеринг на сервере (SSR), который выполняют приложения на PHP или Ruby. Когда браузер запрашивает URL с включённым универсальным рендерингом, Nuxt выполняет код JavaScript (Vue.js) в серверной среде и возвращает браузеру полностью отрисованную HTML-страницу. Nuxt также может вернуть такую страницу из кэша, если она была сгенерирована заранее. Пользователи сразу получают весь начальный контент приложения — в отличие от рендеринга только на клиенте.
После загрузки HTML-документа браузер его обрабатывает, и Vue.js берёт управление документом. Тот же JavaScript-код, который уже выполнялся на сервере, снова запускается на клиенте (в браузере) в фоновом режиме и обеспечивает интерактивность (отсюда название универсальный рендеринг): к HTML подключаются обработчики событий. Это называется гидратация. После завершения гидратации страница получает преимущества вроде динамического интерфейса и клиентских переходов между страницами.
Универсальный рендеринг позволяет Nuxt-приложению обеспечить быструю загрузку страницы, сохраняя при этом преимущества рендеринга на клиенте. Более того, поскольку контент уже присутствует в HTML-документе, поисковые роботы могут индексировать его без дополнительных затрат.
Что отрисовывается на сервере, а что на клиенте?
Естественно задаваться вопросом, какие части Vue-файла выполняются на сервере и/или на клиенте в режиме универсального рендеринга.
<script setup lang="ts">
const counter = ref(0) // выполняется в средах сервера и клиента
const handleClick = () => {
counter.value++ // выполняется только в среде клиента
}
</script>
<template>
<div>
<p>Счётчик: {{ counter }}</p>
<button @click="handleClick">
Увеличить
</button>
</div>
</template>
При первом запросе ref counter инициализируется на сервере, так как он отрисовывается внутри тега <p>. Содержимое handleClick здесь никогда не выполняется. Во время гидратации в браузере ref counter инициализируется снова. Обработчик handleClick в итоге привязывается к кнопке; поэтому можно сделать вывод, что тело handleClick всегда будет выполняться в среде браузера.
Middleware и страницы выполняются на сервере и на клиенте во время гидратации. Плагины могут выполняться на сервере, на клиенте или на обоих. Компоненты при необходимости можно принудительно оставить только на клиенте. Композаблы и утилиты выполняются в зависимости от контекста использования.
Преимущества рендеринга на сервере (SSR):
- Производительность: пользователи сразу получают доступ к содержимому страницы, потому что браузер отображает статический контент быстрее, чем контент, целиком сгенерированный JavaScript. При этом Nuxt сохраняет интерактивность веб-приложения в процессе гидратации.
- SEO: универсальный рендеринг отдаёт браузеру весь HTML страницы, как классическое серверное приложение. Поисковые роботы могут напрямую индексировать контент страницы, поэтому универсальный рендеринг — удачный выбор для любого контента, который нужно быстро проиндексировать.
Недостатки рендеринга на сервере:
- Ограничения разработки: Среды сервера и браузера не предоставляют одни и те же API, и может быть сложно написать код, который мог бы беспрепятственно выполняться на обеих сторонах. К счастью, Nuxt предоставляет рекомендации и специальные переменные, которые помогут вам определить, где выполняется фрагмент кода.
- Стоимость: Чтобы отображать страницы «на лету», должен быть запущен сервер. Это добавляет ежемесячные расходы, как и у любого традиционного сервера. Однако количество вызовов сервера значительно сокращается благодаря универсальному рендерингу, когда браузер берёт на себя навигацию на стороне клиента. Снижение затрат возможно за счет использования пограничного рендеринга.
Универсальный рендеринг очень гибок и может подойти практически для любого варианта использования и особенно подходит для любых контентно-ориентированных веб-сайтов: блоги, маркетинговые веб-сайты, портфолио, сайты электронной коммерции и торговые площадки.
Рендеринг на клиенте {#client-side-rendering}
Из коробки традиционное Vue.js приложение рендерится в браузере (или на клиенте). После того, как браузер загрузит и спарсит весь JavaScript код, содержащий инструкции для создания текущего интерфейса, Vue.js генерирует HTML-элементы.
Преимущества рендеринга на клиенте:
- Скорость разработки: При работе полностью на клиенте нам не нужно беспокоиться о совместимости кода с сервером, например, при использовании API, доступного только для браузера, такого как объект
window. - Дешевле: Запуск сервера увеличивает стоимость инфраструктуры, поскольку вам потребуется работать на платформе, поддерживающей JavaScript. Мы можем размещать клиентские приложения на любом статическом сервере с файлами HTML, CSS и JavaScript.
- Офлайн: поскольку код полностью выполняется в браузере, он может продолжать работать, даже если интернет недоступен.
Недостатки рендеринга на клиенте:
- Производительность: Пользователю приходится ждать, пока браузер загрузит, проанализирует и запустит файлы JavaScript. В зависимости от сети, в которой выполняется загрузка, и устройства пользователя, на котором выполняется анализ и выполнение, это может занять некоторое время и повлиять на пользовательский опыт.
- Поисковая оптимизация: Индексирование и обновление контента, доставленного посредством рендеринга на клиенте, занимает больше времени, чем при обработке HTML-документа, отрисованного на сервере. Это связано с недостатком производительности, который мы обсуждали, поскольку поисковые роботы не будут ждать полной визуализации интерфейса при первой попытке индексировать страницу. Вашему контенту потребуется больше времени, чтобы отобразиться и обновиться на страницах результатов поиска с рендерингом полностью на клиенте.
Рендеринг на клиенте — хороший выбор для очень интерактивных веб-приложений, которые не нуждаются в индексации или которые пользователи посещают часто. Он может использовать кэширование браузера, чтобы пропускать этап загрузки при последующих посещениях, например, SaaS, приложений бэк-офиса или онлайн-игр.
Вы можете включить рендеринг только на клиенте с помощью Nuxt в вашем nuxt.config.ts:
export default defineNuxtConfig({
ssr: false,
})
ssr: false, вы должны поместить в ~/app/spa-loading-template.html HTML-файл с некоторым HTML-кодом, который вы хотите использовать для рендеринга экрана загрузки, который будет отображаться до тех пор, пока ваше приложение не будет гидратировано.Развёртывание статического приложения, отрисованного на стороне клиента
Если вы размещаете приложение на статическом хостинге с помощью команд nuxt generate или nuxt build --prerender, то по умолчанию Nuxt будет рендерить каждую страницу как отдельный статический HTML-файл.
nuxt generate или nuxt build --prerender, вы не сможете использовать серверные эндпоинты: в папку сборки не попадёт сервер. Если нужна серверная функциональность, используйте nuxt build.Если вы используете рендеринг полностью на клиенте, тогда это может быть ненужно. Возможно, вам понадобится только один index.html файл, плюс запасные 200.html и 404.html, которые вы можете указать своему статическому веб-хосту для обслуживания всех запросов.
Чтобы добиться этого, мы можем поменять способ рендеринга маршрутов. Просто добавьте это к вашим хукам в nuxt.config.ts:
export default defineNuxtConfig({
hooks: {
'prerender:routes' ({ routes }) {
routes.clear() // Не создаёт маршрутов (кроме значений по умолчанию)
},
},
})
Это создаст три файла:
index.html200.html404.html
200.html и 404.html могут быть полезны для используемого вами хостинг-провайдера.
Пропуск генерации клиентского fallback
При пререндеринге приложения с рендерингом только на клиенте Nuxt по умолчанию создаёт файлы index.html, 200.html и 404.html. Если нужно запретить генерацию любого (или всех) из этих файлов в сборке, используйте хук 'prerender:generate' из Nitro.
export default defineNuxtConfig({
ssr: false,
nitro: {
hooks: {
'prerender:generate' (route) {
const routesToSkip = ['/index.html', '/200.html', '/404.html']
if (routesToSkip.includes(route.route)) {
route.skip = true
}
},
},
},
})
Гибридный рендеринг {#hybrid-rendering}
Гибридный рендеринг допускает разные правила кэширования для каждого маршрута, используя правила маршрутизации, и решает, как сервер должен отвечать на новый запрос по заданному URL.
Раньше каждый маршрут/страница Nuxt-приложения и сервер должны были использовать одинаковый режим рендеринга, универсальный или клиентский. В различных случаях некоторые страницы должны быть созданы во время сборки, в то время как другие должны быть отрисованы на клиенте. Например, подумайте о контентных сайтах с админ-частью. Каждая страница с контентом должна быть в первую очередь статической и генерироваться один раз, но админ-часть требует регистрации и ведет себя скорее как динамическое приложение.
Nuxt включает в себя правила маршрутизации и поддержку гибридного рендеринга. Используя правила маршрутизации, вы можете определить правила для группы маршрутов Nuxt, меняя режим рендеринга или назначая стратегию кэширования, основанную на маршруте!
Nuxt-сервер будет автоматически регистрировать соответствующие middleware и оборачивать маршруты обработчиками кэша, используя слой кэширования Nitro.
export default defineNuxtConfig({
routeRules: {
// Домашняя страница предварительно отрисовывается во время сборки
'/': { prerender: true },
// Страница продуктов создается по требованию, повторно проверяется в фоновом режиме, кэшируется до изменения ответа API
'/products': { swr: true },
// Страницы продукта создается по требованию, повторно проверяется в фоновом режиме, кэшируется на один час (3600 секунд)
'/products/**': { swr: 3600 },
// Страница сообщений в блоге создается по требованию, повторно проверяется в фоновом режиме, кэшируется на CDN на один час (3600 секунд)
'/blog': { isr: 3600 },
// Страница сообщения в блоге создается по требованию до следующего развертывания, кэшируется на CDN
'/blog/**': { isr: true },
// Панель управления администратора рендерится только на клиенте
'/admin/**': { ssr: false },
// Добавляет CORS-заголовки на маршрутах API
'/api/**': { cors: true },
// Перенаправляет старые URL-адреса
'/old-page': { redirect: '/new-page' },
},
})
Правила маршрутизации
Вы можете использовать следующие свойства:
redirect: string- Определяет перенаправления на сервере.ssr: boolean- Отключает серверный рендеринг HTML для разделов вашего приложения, чтобы они рендерились только в браузере.ssr: false.cors: boolean- Автоматически добавляет cors-заголовки сcors: true- вы можете настроить вывод, переопределив с помощьюheaders.headers: object- Добавляет заданные заголовки в разделы вашего сайта, например к статическим ресурсам.swr: number | boolean- Добавляет заголовки кэша к ответу сервера и кэширует его на сервере или обратном прокси-сервере для настраиваемого TTL (time to live - времени жизни). Настройка Nitronode-serverспособна кэшировать полный ответ. По истечении срока жизни (TTL) будет отправлен кэшированный ответ, а страница будет перегенерирована в фоновом режиме. Если используетсяtrue, будет добавлен заголовокstale-while-revalidateбез MaxAge.isr: number | boolean- Поведение схоже сswr, за исключением того, что ответ можно положить в кэш CDN на поддерживающих платформах (сейчас Netlify или Vercel). Приtrueконтент в CDN сохраняется до следующего развёртывания.prerender: boolean- Предварительно отрисовывает маршруты во время сборки и включает их в сборку как статические ресурсы.noScripts: boolean- Отключает рендеринг скриптов Nuxt и подсказок ресурсов JS для разделов вашего сайта.appMiddleware: string | string[] | Record<string, boolean>- Задаёт промежуточное ПО маршрута (middleware), которое должно или не должно выполняться для путей страниц во Vue-части приложения (не для маршрутов Nitro).
По возможности правила маршрутизации будут автоматически сопоставляться с правилами платформы развёртывания для лучшей производительности (сейчас — Netlify и Vercel).
nuxt generate.Примеры:
Пограничный рендеринг {#edge-side-rendering}
Пограничный рендеринг (Edge-Side Rendering - ESR) — это мощная функция, представленная в Nuxt, которая позволяет отображать ваше приложение Nuxt ближе к вашим пользователям через пограничные серверы сети доставки контента (CDN). Используя ESR, вы можете обеспечить повышенную производительность и сокращение задержек, тем самым обеспечивая улучшенный пользовательский опыт.
При использовании ESR процесс рендеринга переносится на «край» сети — пограничные серверы CDN. Обратите внимание, что ESR — это скорее цель развертывания, чем реальный режим рендеринга.
Когда делается запрос страницы, вместо того, чтобы дойти до исходного сервера, он перехватывается ближайшим пограничным сервером. Этот сервер генерирует HTML для страницы и отправляет его обратно пользователю. Этот процесс минимизирует физическое расстояние, которое должны пройти данные, уменьшая задержку и ускоряя загрузку страницы.
Пограничный рендеринг возможен благодаря Nitro — серверному движку — который обеспечивает работу Nuxt. Он предлагает кроссплатформенную поддержку Node.js, Deno, Cloudflare Workers и других.
Текущие платформы, на которых вы можете использовать ESR:
- Cloudflare Pages с нулевой конфигурацией с использованием git-интеграции и команды
nuxt build - Vercel Edge Functions с использованием команды
nuxt buildи переменной окруженияNITRO_PRESET=vercel-edge - Netlify Edge Functions с использованием команды
nuxt buildи переменной окруженияNITRO_PRESET=netlify-edge
Обратите внимание, что гибридный рендеринг можно использовать при использовании пограничного рендеринга с правилами маршрутизации.
Вы можете изучить примеры с открытым исходным кодом, развернутые на некоторых платформах, упомянутых выше: