Настройка SSR для Riot.js

Server-Side Rendering (SSR) в контексте Riot.js позволяет рендерить компоненты на сервере до того, как они будут отправлены клиенту. Это повышает производительность загрузки страницы, улучшает SEO и снижает нагрузку на клиентскую часть приложения.


Установка и подготовка окружения

Для настройки SSR потребуется Node.js и пакет riot. Начальная установка осуществляется через npm:

npm install riot express @riotjs/ssr
  • riot – основной фреймворк для работы с компонентами.
  • express – серверный фреймворк для Node.js.
  • @riotjs/ssr – модуль для серверного рендеринга компонентов Riot.

Создается базовая структура проекта:

project/
├─ src/
│  ├─ components/
│  │  └─ App.riot
│  └─ server.js
├─ package.json
└─ index.html

Создание компонентов Riot.js

Riot-компоненты имеют расширение .riot и включают три блока: template, script, style. Например, основной компонент App.riot:

<app>
  <h1>{ opts.title }</h1>
  <p>{ message }</p>

  <script>
    export default {
      message: 'Привет, SSR с Riot.js!'
    }
  </script>

  <style>
    h1 { color: #2c3e50; }
    p { font-size: 16px; }
  </style>
</app>
  • opts – объект с параметрами, передаваемыми компоненту при инициализации.
  • export default – описывает состояние компонента.

Настройка серверного рендеринга

Для SSR используется модуль @riotjs/ssr. Основная идея заключается в том, чтобы компилировать компонент в HTML на сервере и отправлять его клиенту.

Пример server.js:

import express from 'express'
import { render } from '@riotjs/ssr'
import App from './components/App.riot'

const app = express()

app.get('/', async (req, res) => {
  const html = await render(App, { title: 'Главная страница' })
  res.send(`
    <!DOCTYPE html>
    <html lang="ru">
      <head>
        <meta charset="UTF-8">
        <title>Riot SSR</title>
      </head>
      <body>
        ${html}
        <script type="module" src="/client.js"></script>
      </body>
    </html>
  `)
})

app.listen(3000, () => console.log('Server started on http://localhost:3000'))
  • render(App, { title: '...' }) – рендерит компонент App в HTML с указанными опциями.
  • Возвращаемый html уже содержит готовую разметку для страницы.

Интеграция с клиентской частью

После SSR компонент должен быть “гидрирован” на клиенте, чтобы работали интерактивные функции:

import { component } from 'riot'
import App from './components/App.riot'

component(App)(document.querySelector('app'))
  • Метод component(App) создаёт экземпляр компонента.
  • Передача элемента DOM (document.querySelector('app')) позволяет сохранить серверную разметку и подключить клиентскую логику без перерисовки.

Асинхронная загрузка данных

Для сложных приложений данные часто подгружаются на сервере перед рендерингом:

app.get('/users', async (req, res) => {
  const users = await fetchUsersFromDB()
  const html = await render(App, { title: 'Пользователи', users })
  res.send(html)
})
  • fetchUsersFromDB – асинхронная функция, возвращающая данные.
  • Передача данных через объект opts позволяет компоненту использовать их сразу при рендеринге на сервере.

Работа с маршрутизацией

Riot.js не включает встроенный роутер, но SSR совместим с любым серверным маршрутизатором, например Express. Маршрутизация осуществляется на сервере:

app.get('/:page', async (req, res) => {
  const page = req.params.page
  const html = await render(App, { title: `Страница ${page}` })
  res.send(html)
})
  • Каждому маршруту можно передавать уникальные данные для рендеринга.
  • Это обеспечивает динамическое создание HTML для каждой страницы.

Оптимизация и кеширование

Для повышения производительности SSR компонентов рекомендуется:

  • Кэшировать HTML-результат для часто посещаемых страниц.
  • Минимизировать вычисления данных на сервере.
  • Использовать ленивую загрузку тяжелых компонентов на клиенте.

Пример простого кеширования:

const cache = {}

app.get('/news', async (req, res) => {
  if (cache.news) {
    return res.send(cache.news)
  }

  const newsData = await fetchNews()
  const html = await render(App, { title: 'Новости', news: newsData })
  cache.news = html
  res.send(html)
})

Особенности и ограничения SSR в Riot.js

  • Состояние компонента: любые изменения состояния после рендеринга должны выполняться на клиенте.
  • Стили: встроенные стили .riot автоматически включаются в HTML при SSR.
  • События DOM: обработчики событий подключаются только на клиентской стороне.
  • Совместимость с внешними библиотеками: сторонние модули, использующие window или document, требуют проверки на сервере.

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