Server-side rendering (SSR) в контексте Kepler.gl представляет собой попытку перенести часть визуализации геопространственных данных из браузера на сервер, сохраняя при этом согласованность состояния приложения и обеспечивая генерацию статических результатов: изображений карт, HTML-снимков интерфейса или предрассчитанных слоёв данных. Основная сложность заключается в том, что Kepler.gl опирается на WebGL, React и browser-only API, которые изначально не предназначены для выполнения в Node.js среде.
Kepler.gl построен поверх React и deck.gl, а рендеринг карты осуществляется через WebGL-контекст. Это приводит к нескольким фундаментальным ограничениям:
window, document,
navigator в Node.jsПопытка прямого вызова компонентов Kepler.gl на сервере приводит к ошибкам инициализации, связанным с отсутствием графического контекста. Поэтому SSR в классическом смысле React (HTML-строка через renderToString) применим только к оболочке интерфейса, но не к карте как визуальному WebGL-объекту.
SSR в Kepler.gl обычно разделяется на три уровня:
Такое разделение позволяет использовать SSR для подготовки данных и интерфейса, но переносить графическую часть в отдельный процесс.
Kepler.gl хранит состояние визуализации в Redux store. Это состояние включает:
Серверная часть может подготовить это состояние и передать его на клиент или в рендер-процесс:
const initialState = {
keplerGl: {
mapState: {
latitude: 52.3,
longitude: 104.3,
zoom: 10,
bearing: 0,
pitch: 0
},
mapStyle: {
styleType: 'dark'
},
datasets: [
{
info: { label: 'Data' },
data: rawData
}
]
}
};
Такое состояние является ключевым элементом SSR-архитектуры, поскольку оно отделяет данные от визуального слоя.
Основное препятствие серверного рендеринга Kepler.gl — отсутствие WebGL. Для обхода используются headless-решения:
Каждое решение решает задачу по-разному:
headless-gl эмулирует WebGL контекст в Node.jsnode-canvas заменяет 2D canvas, но не WebGL
полностьюpuppeteer запускает полноценный браузер без UIНа практике наиболее стабильным вариантом считается использование headless Chromium, так как он обеспечивает полноценную поддержку WebGL.
Один из распространённых подходов — рендеринг Kepler.gl в headless Chrome и экспорт результата в изображение или HTML-снимок.
Процесс включает следующие этапы:
Пример:
import puppeteer from 'puppeteer';
async function renderMap() {
const browser = await puppeteer.launch({
headless: 'new'
});
const page = await browser.newPage();
await page.goto('http://localhost:3000/map');
await page.evaluate((state) => {
window.__KEPLER_STATE__ = state;
}, initialState);
await page.waitForSelector('canvas');
await page.waitForTimeout(2000);
const canvas = await page.$('canvas');
await canvas.screenshot({ path: 'map.png' });
await browser.close();
}
Этот подход фактически не является SSR в классическом смысле, но выполняет серверный рендеринг графического результата.
Альтернативный подход заключается в использовании WebGL-эмуляции:
gl (headless-gl)Пример инициализации:
import { createGLContext } from 'gl';
const gl = createGLContext(800, 600, {
preserveDrawingBuffer: true
});
Далее deck.gl может использовать этот контекст для рендеринга слоёв. Однако Kepler.gl напрямую не рассчитан на такой режим, поэтому требуется значительная модификация внутреннего pipeline.
Kepler.gl использует deck.gl как визуальный слой. deck.gl поддерживает headless rendering через offscreen contexts.
Базовый процесс:
Проблема заключается в том, что Kepler.gl добавляет абстракции поверх deck.gl, и прямой доступ к слоям затруднён.
Одним из наиболее практичных сценариев серверного рендеринга является генерация статических изображений карт:
Это используется для:
Процесс:
Ключевой момент — ожидание завершения всех асинхронных операций (тайлы Mapbox, шейдеры, загрузка данных).
SSR в Kepler.gl часто используется не для рендера графики, а для подготовки данных:
Пример серверной агрегации:
function preprocess(points) {
return points.reduce((acc, p) => {
const key = `${Math.floor(p.lat)}_${Math.floor(p.lng)}`;
if (!acc[key]) acc[key] = 0;
acc[key]++;
return acc;
}, {});
}
Такой подход снижает нагрузку на клиент и ускоряет отрисовку Kepler.gl.
После серверной подготовки состояния происходит гидратация:
Критично соблюдать идентичность структуры состояния, иначе Kepler.gl пересобирает карту с нуля, теряя оптимизации.
const store = createStore(
rootReducer,
window.__INITIAL_STATE__
);
Kepler.gl использует Mapbox tiles, которые загружаются через сеть. В SSR это создаёт дополнительные сложности:
Поэтому часто применяется стратегия:
На практике выделяются несколько архитектур:
1. Thin SSR
2. Hybrid SSR
3. Headless rendering pipeline
Каждый подход выбирается в зависимости от требований к интерактивности и нагрузке.
Для повышения стабильности SSR применяются техники:
Особенно важно отключать интерполяции и transition-анимации, которые требуют реального времени.
Одна из ключевых проблем SSR — момент, когда карта считается «готовой». WebGL не предоставляет явного события завершения рендеринга, поэтому используются эвристики:
Эта неопределённость делает SSR в Kepler.gl ближе к рендеринг-пайплайну, чем к классическому React SSR.