логотип PurpleSchool
логотип PurpleSchool

Пять шагов по настройке 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, шаги гораздо проще:

  1. В корне проекта выполните:
    bash npm run dev
  2. Для 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’ы и частичный рендеринг компонентов.

Стрелочка влевоРабота с teleport для управления DOM во VueИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateИспользование query-параметров и их обработка в маршрутах VueРазрешение конфликтов и ошибок с помощью Vue resolveЗагрузка и управление состоянием загрузки в VueРабота с JSON данными в приложениях VueИспользование библиотек Vue для расширения функционалаКак работать с экземплярами компонента Instance во VueЭкспорт и импорт данных и компонентов в VueПолучение данных и API-запросы во Vue.jsОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueОбзор популярных шаблонов и стартовых проектов на VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентов3 способа улучшить навигацию Vue с push()Использование scoped стилей для изоляции CSS в компонентах VueОбработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний