Концепция Server-Side Rendering

Server-Side Rendering (SSR) представляет собой подход, при котором HTML-страница формируется на сервере, а не в браузере клиента. В контексте Vue.js это означает, что Vue-компоненты рендерятся на сервере, и готовый HTML отправляется пользователю, что повышает скорость первичной загрузки и улучшает SEO.


Основные принципы SSR

  1. Предварительный рендеринг на сервере В отличие от классического клиентского рендеринга, где весь процесс рендеринга происходит в браузере, SSR формирует полноценный HTML на сервере. Когда браузер получает страницу, она уже содержит готовую разметку, а Vue на клиенте «гидратирует» её, превращая статический HTML в интерактивный интерфейс.

  2. Гидратация (Hydration) Гидратация — это процесс, при котором Vue на клиенте связывает уже существующий серверный HTML с виртуальным DOM. В результате динамические возможности Vue (реактивность, события) становятся доступными, но при этом пользователь сразу видит содержимое страницы, не дожидаясь загрузки JavaScript.

  3. Разделение клиентского и серверного кода Для SSR необходимо различать код, который может выполняться на сервере, и код, требующий доступа к браузерным API (например, window или document). Использование универсальных (isomorphic) компонентов позволяет безопасно рендерить их как на сервере, так и на клиенте.


Настройка SSR в Vue.js

Vue предоставляет отдельный пакет @vue/server-renderer для серверного рендеринга и фреймворк Nuxt.js для упрощения SSR-приложений. Основные этапы настройки:

  1. Создание универсального Vue-приложения Универсальное приложение должно экспортировать функцию, возвращающую новый экземпляр Vue, чтобы каждый запрос создавал отдельный рендеринг:

    import { createSSRApp } from 'vue'
    import App from './App.vue'
    
    export function createApp() {
      const app = createSSRApp(App)
      return { app }
    }
  2. Рендеринг на сервере Серверный рендеринг выполняется с помощью renderToString, который возвращает готовый HTML:

    import { renderToString } from '@vue/server-renderer'
    import { createApp } from './app'
    
    const { app } = createApp()
    
    renderToString(app).then(html => {
      console.log(html) // готовый HTML для отправки клиенту
    })
  3. Гидратация на клиенте На клиенте Vue «подхватывает» серверный HTML с помощью createApp и mount:

    import { createApp } from './app'
    
    const { app } = createApp()
    app.mount('#app') // элемент с уже существующей серверной разметкой

Работа с данными при SSR

Одной из ключевых задач SSR является загрузка данных до рендеринга. Для этого используется асинхронная функция asyncData или подход с предварительной загрузкой данных в setup или в Vuex.

Пример с использованием Vuex:

// store.js
export const store = {
  state: () => ({ items: [] }),
  actions: {
    async fetchItems({ commit }) {
      const response = await fetch('https://api.example.com/items')
      const data = await response.json()
      commit('setItems', data)
    }
  },
  mutations: {
    setItems(state, items) {
      state.items = items
    }
  }
}

// server.js
import { store } from './store'
await store.dispatch('fetchItems')

Загрузка данных на сервере позволяет отдать пользователю полностью сформированную страницу с уже доступной информацией.


Преимущества и ограничения SSR

Преимущества:

  • Быстрая первая загрузка страницы
  • Улучшенное SEO благодаря готовой разметке
  • Лучшая поддержка старых устройств и слабых сетей

Ограничения:

  • Более сложная конфигурация сервера
  • Возможные проблемы с доступом к браузерным API
  • Увеличение нагрузки на сервер при большом количестве запросов

Взаимодействие с динамическим контентом

Для SSR важно учитывать, что браузерный JavaScript может выполнять динамическое обновление DOM только после гидратации. Поэтому любые операции, зависящие от window или пользовательских событий, следует выполнять только на клиенте:

if (typeof window !== 'undefined') {
  // код, доступный только в браузере
}

Интеграция с маршрутизацией

SSR обычно сочетается с Vue Router в режиме history. Для каждого запроса на сервере создаётся новый экземпляр маршрутизатора:

import { createRouter, createMemoryHistory } from 'vue-router'
import routes from './routes'

export function createApp() {
  const app = createSSRApp(App)
  const router = createRouter({
    history: createMemoryHistory(),
    routes
  })
  app.use(router)
  return { app, router }
}

После этого сервер может рендерить нужный маршрут на основе URL-запроса.


Использование Nuxt.js для упрощения SSR

Nuxt.js автоматизирует большинство задач SSR: конфигурацию сервера, маршрутизацию, гидратацию, управление данными. В Nuxt можно сосредоточиться на компонентах и страницах, а SSR будет обеспечиваться «из коробки». Nuxt поддерживает asyncData, fetch, модули Vuex и динамические маршруты, что упрощает создание SEO-оптимизированных приложений.


Оптимизация SSR-приложений

  1. Кэширование HTML Для часто посещаемых страниц можно кэшировать готовый HTML, чтобы снизить нагрузку на сервер.

  2. Ленивая загрузка компонентов Использование динамического импорта (defineAsyncComponent) позволяет разделять код и загружать только необходимые компоненты.

  3. Минификация и сжатие Сервер должен отдавать минифицированный HTML и включать gzip или brotli-сжатие для ускорения передачи данных.

  4. Контроль ресурсов SSR увеличивает использование CPU на сервере, поэтому необходимо балансировать между рендерингом на сервере и клиенте.


Server-Side Rendering в Vue.js объединяет преимущества быстрого отображения контента и полной функциональности Vue, обеспечивая высокую производительность и SEO-оптимизацию. Использование SSR требует внимания к архитектуре приложения, работе с данными и маршрутизацией, но позволяет создавать профессиональные масштабируемые приложения с богатым пользовательским интерфейсом.