Изоморфный код

Изоморфный код (isomorphic code) — это код, который может выполняться как на клиенте, так и на сервере, обеспечивая единый интерфейс для рендеринга компонентов. В контексте SkateJS это позволяет создавать веб-компоненты, которые могут предварительно отрисовываться на сервере, улучшая производительность и SEO, при этом сохраняя динамическую интерактивность на клиенте.

Основы изоморфного рендеринга

SkateJS построен на основе веб-компонентов и реактивной модели данных. Ключевой особенностью является возможность регистрировать компоненты и рендерить их без непосредственного доступа к DOM. Это достигается через использование рендер-функций, которые принимают данные и возвращают виртуальные узлы (Virtual DOM).

import { define, h, Component } from 'skatejs';

class MyComponent extends Component {
  static get props() {
    return {
      message: String
    };
  }

  renderCallback() {
    return <div>{this.props.message}</div>;
  }
}

define(MyComponent);

В этом примере renderCallback возвращает JSX-узел, который может быть использован как на сервере, так и на клиенте. На сервере его можно преобразовать в HTML, а на клиенте — интегрировать в реальный DOM.

Серверная подготовка компонентов

Для серверного рендеринга компонента используется метод renderToString или аналогичная функция, позволяющая превратить виртуальный DOM в HTML-строку.

import { renderToString } from 'skatejs/ssr';

const html = renderToString(<my-component message="Привет, мир!" />);
console.log(html); 
// <my-component message="Привет, мир!"><div>Привет, мир!</div></my-component>

Результат рендеринга включает тег компонента и внутреннее содержимое, что позволяет браузеру корректно инициализировать его при загрузке.

Инициализация на клиенте

После загрузки HTML на клиенте SkateJS автоматически подхватывает серверный рендеринг и продолжает управление компонентами. Это достигается через авто-гидратацию, когда компоненты, уже присутствующие в DOM, получают свои свойства и события.

document.addEventListener('DOMContentLoaded', () => {
  define(MyComponent); // Автоматически инициализирует компоненты в DOM
});

Таким образом, серверный HTML плавно переходит в полностью интерактивный клиентский компонент без повторной генерации DOM.

Управление состоянием в изоморфных компонентах

Особенность изоморфного подхода заключается в том, что состояние компонента должно быть сериализуемым и переносимым между сервером и клиентом. SkateJS использует props и реактивные свойства для синхронизации данных.

class Counter extends Component {
  static get props() {
    return {
      count: Number
    };
  }

  increment() {
    this.props.count++;
  }

  renderCallback() {
    return (
      <div>
        <span>{this.props.count}</span>
        <button oncl ick={() => this.increment()}>Увеличить</button>
      </div>
    );
  }
}

Для изоморфного кода важно передавать начальное состояние с сервера:

const initialState = { count: 10 };
const html = renderToString(<counter {...initialState} />);

На клиенте этот объект используется для гидратации:

const counterElement = document.querySelector('counter');
Object.assign(counterElement, initialState);
define(Counter);

Преимущества изоморфного подхода в SkateJS

  1. SEO и производительность: контент доступен сразу в HTML.
  2. Единая логика: один компонент используется на сервере и клиенте.
  3. Плавная гидратация: отсутствие мерцания и повторной генерации DOM.
  4. Сериализация состояния: обеспечивает предсказуемую синхронизацию данных.

Особенности работы с веб-компонентами

  • Shadow DOM: при серверной генерации Shadow DOM не создаётся, поэтому изоморфный HTML остаётся простым и доступным для SEO.
  • События: на сервере события не обрабатываются, они подключаются только на клиенте после гидратации.
  • Асинхронные данные: для серверного рендеринга данные должны быть подготовлены заранее, либо использовать промисы с рендерингом после их разрешения.

Рекомендации по проектированию изоморфных компонентов

  • Стараться держать чистую логику рендеринга: renderCallback не должен зависеть от window или document.
  • Разделять состояние и представление: сервер получает только сериализуемое состояние.
  • Использовать примитивные данные и JSON для передачи между сервером и клиентом.
  • Планировать гидратацию событий и методов, чтобы клиентская интерактивность была идентична серверной.

Изоморфный код в SkateJS позволяет создавать высокопроизводительные и SEO-дружелюбные приложения, где один и тот же компонент служит и для рендеринга на сервере, и для интерактивного поведения на клиенте. Это делает архитектуру более единообразной, а поддержку компонентов — проще.