Олег Марков
Пять шагов по настройке SSR в Vuejs
Введение
Разрабатваете Vue-приложение и хотите повысить его производительность, улучшить SEO и ускорить первый рендер для пользователей? Серверный рендеринг (SSR) — именно та технология, которая поможет решить эти задачи. Серверный рендеринг позволяет создавать HTML-контент заранее на сервере, а не только на стороне клиента — благодаря этому поисковые системы индексируют страницы лучше, первое отображение приложения становится моментальным, и значительно сокращается Time To First Byte.
В этой статье я покажу вам пять пошаговых этапов настройки SSR для Vue.js-приложения. Мы рассмотрим как индивидуальные настройки со своим сервером на Node.js, так и современные подходы с использованием фреймворка Nuxt.js — ведь это основной инструмент SSR в экосистеме Vue. Каждый шаг снабжен пояснениями и примерами — вместе на практике вы сможете построить гибкое и эффективное SSR-приложение.
Шаг 1. Понимание основ SSR в Vue.js
Чем SSR отличается от классического SPA
Традиционное приложение на Vue (SPA) полностью рендерится в браузере пользователя. При первой загрузке браузер скачивает лишь простой index.html
и большой JavaScript-файл, а всё остальное подгружается и формируется на клиенте. Такой подход даёт гибкость, но страдает низкой скоростью первого отображения и плохой индексацией, если вы делаете публичный сайт.
С SSR всё иначе: полноценный HTML страницы отдается сразу с сервера. У пользователя мгновенно появляется интерактивная страница. После первой загрузки Vue «примонтируется» к уже отрендеренному HTML и продолжит работать как обычное SPA (этот процесс называется "гидратация").
Когда использовать SSR
Включать SSR стоит в проектах, где важны сумма следующих факторов:
- Приложение публичное и критично для SEO
- Важна минимизация Time To First Paint
- Много динамического контента на страницах (например, интернет-магазины, блоги)
- Для внутренних админок, где SEO не нужен, SSR, как правило, избыточен.
Шаг 2. Установка исходного проекта и необходимых зависимостей
Базовая установка Vue CLI
Начнем с создания базового проекта Vue. Использовать можно Vue CLI или Vite (последний более современный).
# Для Vue CLI
npm install -g @vue/cli
vue create vue-ssr-app
cd vue-ssr-app
Установка зависимостей для SSR
Для организации серверного рендеринга потребуются дополнительные пакеты. Если вы хотите настраивать всё «вручную», вам понадобятся:
vue
vue-server-renderer
express
или другой Node.js-фреймворк
Устанавливаем зависимости:
npm install vue vue-server-renderer express
Для Nuxt.js
Если хочется идти более «батарейки в комплекте» способом, используйте Nuxt.js. Его инфраструктура уже включает SSR «из коробки», вам нужно только:
npx nuxi init nuxt-ssr-app
cd nuxt-ssr-app
npm install
Вы можете выбрать любой вариант, который кажется понятным.
Шаг 3. Создание серверной части для SSR
Если вы решили собрать SSR-приложение без Nuxt, давайте рассмотрим организацию серверной части на Express.
Создание Vue-экземпляра для рендеринга
Создайте файл src/app.js
:
// src/app.js
import Vue from 'vue'
import App from './App.vue'
export default function createApp() {
return new Vue({
render: h => h(App),
})
}
Создание серверного рендерера
Добавьте сервер: файл server.js
в корне проекта.
// server.js
const express = require('express')
const fs = require('fs')
const path = require('path')
const { createBundleRenderer } = require('vue-server-renderer')
const createApp = require('./src/app').default
const server = express()
const renderer = require('vue-server-renderer').createRenderer({
template: fs.readFileSync(path.resolve(__dirname, 'index.template.html'), 'utf-8'),
})
// Обработка всех маршрутов
server.get('*', (req, res) => {
const app = createApp()
const context = { url: req.url }
renderer.renderToString(app, context, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error')
return
}
res.end(html)
})
})
server.listen(8080)
Пример шаблона
Создайте index.template.html
:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Vue SSR App</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
Всё готово к запуску! Статические стили, скрипты и ассеты можно прокидывать через сервер Express.
Шаг 4. Асинхронные данные и маршрутизация (router, store) в SSR
SSR и асинхронные данные
Тонкость SSR — данные, которые должны быть загружены ДО рендера на сервере. Для этого в компонентах Vue используют функцию asyncData
или специальные хуки.
Пример получения данных
Допустим, у нас есть страница, которая должна загрузить статьи с API до отдачи HTML-пользователю. Реализуем это с помощью создания отдельного метода для загрузки данных:
// src/App.vue
<template>
<div>
<h1>Список статей</h1>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
posts: []
}
},
asyncData({ route }) {
// Здесь делается асинхронный запрос на сервер
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(posts => ({ posts }))
},
created() {
// Если SSR, данные уже получены
if (this.$options.asyncData) {
Object.assign(this, this.$options.asyncData)
}
}
}
</script>
На практике в чистом Vue приходится реализовать такую прокладку самому. В Nuxt функция asyncData
работает буквально «из коробки» именно так.
SSR с Vue Router
Если используется роутер, его тоже надо настраивать для SSR. Пример создания инстанса:
// src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './pages/Home.vue'
import About from './pages/About.vue'
Vue.use(Router)
export function createRouter() {
return new Router({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})
}
Роутер нужно создавать ЗАНОВО для каждого SSR-запроса (иначе между пользователями могут мигрировать состояния).
SSR с Vuex Store
Для хранилища данных также нужен новый инстанс на каждый запрос:
// src/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export function createStore() {
return new Vuex.Store({
state: () => ({
counter: 0
}),
mutations: {
increment(state) { state.counter++ }
}
})
}
Шаг 5. Конфигурация сборки и запуск SSR (Webpack / Vite / Nuxt)
Сборка SPA и SSR бандлов
SSR требует двух разных JavaScript-бандлов:
- Клиентский (для гидратации в браузере)
- Серверный (для рендеринга на сервере)
Для этого в Webpack (или Vite) конфиге используются разные точки входа.
Пример Webpack-конфигов
webpack.client.config.js:
module.exports = {
entry: './src/entry-client.js',
output: {
filename: 'bundle.client.js',
path: __dirname + '/dist/client'
},
// ...другие настройки
}
webpack.server.config.js:
module.exports = {
entry: './src/entry-server.js',
target: 'node',
output: {
filename: 'bundle.server.js',
path: __dirname + '/dist/server',
libraryTarget: 'commonjs2'
},
// ...другие настройки
}
Запуск сервера
После сборки запускается server.js
, который импортирует серверный бандл и шаблон.
SSR в Nuxt.js — проще и удобнее
Если используете Nuxt, шаги гораздо проще:
- В корне проекта выполните:
bash npm run dev
- Для production выполните:
bash npm run build && npm run start
Nuxt сам управляет раздельной сборкой и всем стеком SSR, а вы фокусируетесь только на бизнес-логике приложения и компонентах.
SSR через Vite
В новой экосистеме возможно SSR-сборка через Vite (например, c vite-ssr плагинами). Но для базового понимания Webpack и Nuxt остаются стандартом.
Заключение
Пять шагов настройки сервеного рендеринга в Vue.js включают: понимание основ SSR, базовую настройку проекта и зависимостей, написание серверной части, правильную организацию маршрутизации и асинхронных данных, а также корректную настройку инструментов сборки и запуска. Настроив SSR в вашем Vue-приложении, вы получаете максимум от современных веб-технологий: SEO, скорость рендера, единый подход к работе с данными.
Наиболее удобным и быстрым способом реализации SSR во Vue является Nuxt.js. Он скрывает большинство технических деталей и делает внедрение максимально простым. Однако если вам важен полный контроль или кастомизация, настройка SSR вручную через серверный рендерер тоже остаётся отличной практикой.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как подключить сторонние библиотеки (например, сторонний UI или axios) при SSR?
Сторонние библиотеки нужно подключать только в коде, совместимом с сервером. Если библиотека использует напрямую window
или document
, она "сломается" в среде Node.js. Окружайте такие вызовы проверкой:
if (process.client) {
// window/document используются только на клиенте
}
В Nuxt для этого предусмотрены хуки и переменные среды.
Как делается аутентификация пользователей с SSR?
Вариант — хранить токен сессии в http-only cookie и проверять её на сервере при каждом запросе. На сервере (например, в Express или Nuxt middleware) валидируйте cookie и прокидывайте пользователя в рендер-функции через context.
Почему SSR часто работает медленнее, чем CSR после первого рендера?
SSR нагружает именно сервер, а не браузер клиента. При большом кол-ве одновременных запросов сервер может не справляться, особенно на дешёвом хостинге. Решают через оптимизацию кэша (например, отдавать HTML из генератора только раз в N минут).
Как прокинуть данные пользователя из back-end в клиент при SSR?
Передавайте данные пользователя через context при рендере сервера, добавляя их в начальный state приложения. В Nuxt.js это можно сделать в middleware, в Express — через передачу переменных внутрь template.
Как обновлять только нужные части страницы без полного SSR?
SSG/ISR (статическая генерация или инкрементальная статическая сборка) позволяет рендерить только части сайта по мере их изменения, не обновляя весь сайт. В Nuxt такие возможности предоставляет Nuxt Content, в ручных решениях — через отдельные endpoint’ы и частичный рендеринг компонентов.