В Vue Router существует возможность настройки маршрутов не только на клиенте, но и на сервере, что критично для приложений с серверным рендерингом (SSR). Серверный роутинг позволяет управлять навигацией на стороне сервера до того, как HTML будет отправлен пользователю, обеспечивая правильную генерацию контента для поисковых систем и ускоряя первый рендер страницы.
Для создания роутера на сервере используется та же структура, что и
на клиенте, с небольшими отличиями. Основные шаги включают импорт
createRouter и createMemoryHistory:
import { createRouter, createMemoryHistory } from 'vue-router';
import Home from './pages/Home.vue';
import About from './pages/About.vue';
export function createAppRouter() {
return createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
],
});
}
Ключевые моменты:
createMemoryHistory() вместо
createWebHistory(), так как на сервере нет объекта
window.При SSR важно дождаться разрешения всех асинхронных данных перед
рендерингом страницы. Для этого Vue Router предоставляет метод
router.isReady():
import { createSSRApp } from 'vue';
import { createAppRouter } from './router';
export function createApp() {
const app = createSSRApp(App);
const router = createAppRouter();
app.use(router);
return { app, router };
}
На сервере процесс выглядит так:
export async function render(url) {
const { app, router } = createApp();
router.push(url);
await router.isReady();
const html = await renderToString(app);
return html;
}
Объяснение ключевых действий:
router.push(url) навигирует роутер на нужный путь перед
рендерингом.router.isReady() гарантирует, что все асинхронные
компоненты и хук beforeRouteEnter завершены.Для серверного рендеринга часто используются асинхронные компоненты и хуки для предварительной загрузки данных:
const About = defineAsyncComponent(() =>
import('./pages/About.vue')
);
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About, beforeEnter: async (to, from, next) => {
await fetchDataForAbout();
next();
}},
]
Важно:
beforeEnter позволяет серверу ожидать
загрузку данных, исключая “мерцание” пустого состояния на клиенте.На сервере ошибки роутера должны корректно обрабатываться для возврата нужного HTTP-кода:
router.isReady().catch(err => {
if (err.name === 'NavigationDuplicated') {
// Игнорируем дублирующиеся навигации
} else {
throw err;
}
});
Дополнительно для SSR удобно перехватывать маршруты, которые не совпадают ни с одним путем, чтобы возвращать 404:
router.beforeEach((to, from, next) => {
if (!router.hasRoute(to.path)) {
next('/404');
} else {
next();
}
});
Для серверного рендеринга роутер часто интегрируется с глобальным состоянием (Vuex или Pinia) для предварительной инициализации данных:
const store = createStore();
router.beforeResolve(async (to) => {
const matchedComponents = router.getMatchedComponents(to);
for (const component of matchedComponents) {
if (component.asyncData) {
await component.asyncData(store);
}
}
});
Принцип работы:
asyncData, который наполняет состояние перед
рендерингом.createMemoryHistory() для
серверного роутера.Эти правила обеспечивают правильную работу Vue Router в SSR и позволяют создавать масштабируемые, быстрые и SEO-дружественные приложения.