Code splitting

CesiumJS представляет собой крупную модульную систему, включающую ядро рендеринга, движок сцен, обработку 3D Tiles, работу с террейном, провайдерами изображений, а также набор вспомогательных утилит и веб-воркеров. При подключении библиотеки целиком формируется значительный по объёму JavaScript-бандл, что делает стратегию разделения кода критически важной для производственных приложений.

Code splitting в контексте CesiumJS направлен на уменьшение времени первичной загрузки, оптимизацию использования сети и перенос части вычислений и инициализации на момент реальной необходимости функционала.

Структура CesiumJS как основа для разделения кода

Архитектура CesiumJS включает несколько крупных подсистем, которые логически изолированы и могут загружаться независимо:

  • ядро Viewer и базовые математические утилиты
  • рендеринг сцены (Scene Graph, primitives)
  • система сущностей (Entity API)
  • обработка 3D Tiles
  • провайдеры данных (imagery, terrain)
  • визуальные эффекты (post-processing)
  • веб-воркеры для геометрии и декодирования
  • интеграция Cesium Ion

Каждая подсистема содержит как синхронные модули, так и динамически используемые компоненты. Это создаёт естественные границы для разделения чанков.

Базовые принципы code splitting

Разделение кода в CesiumJS-приложениях обычно опирается на три уровня:

  1. Модульный уровень ES Modules
  2. Бандлерный уровень (Webpack, Vite, Rollup)
  3. Архитектурный уровень приложения (lazy initialization компонентов)

Модульность ES6 позволяет импортировать только необходимые части библиотеки:

import { Viewer } from "cesium/Source/Widgets/Viewer/Viewer.js";

Однако такой подход требует аккуратного управления зависимостями, так как внутренние импорты Cesium могут тянуть дополнительные модули.

Динамический импорт и отложенная загрузка Viewer

Ключевая стратегия заключается в переносе создания Viewer в отдельный чанк:

async function createCesiumViewer(container) {
  const cesiumModule = await import("cesium");

  return new cesiumModule.Viewer(container, {
    terrainProvider: new cesiumModule.EllipsoidTerrainProvider(),
  });
}

Такой подход позволяет исключить Cesium из основного бандла и загрузить его только при переходе к 3D-визуализации.

Разделение по функциональным зонам приложения

CesiumJS часто используется в приложениях с несколькими режимами работы:

  • 2D аналитика
  • 3D визуализация
  • просмотр тайловых слоёв
  • работа с измерениями и аннотациями

Каждый режим может быть выделен в отдельный чанк:

const load3DModule = () => import("./modules/cesium3d/index.js");
const loadMeasurementTools = () => import("./modules/analysis/measurements.js");

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

Работа с 3D Tiles и ленивое подключение данных

Подсистема 3D Tiles является одной из самых тяжёлых частей CesiumJS. Её инициализация часто откладывается до момента появления сцены:

async function loadTileset(url) {
  const cesium = await import("cesium");
  const tileset = await cesium.Cesium3DTileset.fromUrl(url);

  return tileset;
}

Отдельный чанк для 3D Tiles предотвращает загрузку декодеров и обработчиков до фактического запроса данных.

Code splitting в связке с веб-воркерами

CesiumJS активно использует веб-воркеры для геометрических вычислений и декодирования форматов. Эти воркеры могут быть вынесены в отдельные бандлы:

  • geometry worker
  • imagery processing worker
  • terrain quantization worker

При использовании Webpack требуется явное указание путей:

window.CESIUM_BASE_URL = "/cesium/";

И копирование статических ресурсов:

new CopyWebpackPlugin({
  patterns: [
    { from: "node_modules/cesium/Build/Cesium/Workers", to: "Workers" }
  ]
});

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

CESIUM_BASE_URL и управление статическими чанками

CesiumJS использует набор внешних ресурсов: шейдеры, текстуры, воркеры. Эти ресурсы не попадают в JavaScript-бандл и требуют отдельной загрузки.

Корректная настройка базового пути критична для code splitting:

window.CESIUM_BASE_URL = "/assets/cesium/";

Это позволяет отделить статические ресурсы от JS-логики и размещать их в CDN или отдельном домене.

Code splitting в Webpack и Vite для CesiumJS

Webpack

Webpack требует настройки для корректной работы динамических импортов Cesium:

  • исключение Cesium из бандла через externals (при необходимости)
  • настройка alias на исходники
  • копирование ресурсов

Пример разделения:

optimization: {
  splitChunks: {
    chunks: "all",
    cacheGroups: {
      cesium: {
        test: /[\\/]cesium[\\/]/,
        name: "cesium",
        chunks: "all",
      },
    },
  },
}

Vite

Vite обеспечивает более естественное разделение благодаря native ESM:

export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          cesium: ["cesium"],
        },
      },
    },
  },
};

Cesium при этом загружается как отдельный динамический модуль без дополнительной трансформации.

Lazy loading компонентов сцены

Cesium Viewer содержит множество подсистем, которые не обязаны инициализироваться сразу:

  • imagery layers
  • post-processing effects
  • entity collections
  • debug overlays

Отложенная инициализация позволяет уменьшить initial bundle execution cost:

async function enablePostProcessing(viewer) {
  const cesium = await import("cesium");

  viewer.scene.postProcessStages.fxaa.enabled = true;
  viewer.scene.postProcessStages.bloom.enabled = true;
}

Разделение UI и Cesium-движка

В современных приложениях Cesium часто используется внутри UI-фреймворков. Разделение кода между UI и движком снижает блокировку интерфейса.

Типичная структура:

  • core UI bundle
  • Cesium runtime bundle
  • analytics bundle
  • tools bundle

UI и Cesium загружаются независимо:

const loadUI = () => import("./ui/app.js");
const loadCesiumRuntime = () => import("./cesium/runtime.js");

Оптимизация загрузки Imagery и Terrain

ImageryProvider и TerrainProvider могут быть отложены до момента изменения режима отображения.

async function setTerrain(viewer, url) {
  const cesium = await import("cesium");
  viewer.terrainProvider = await cesium.CesiumTerrainProvider.fromUrl(url);
}

Разделение этих компонентов уменьшает стартовую нагрузку на сеть и CPU.

Чанкование по маршрутам приложения

В SPA-архитектуре Cesium обычно подключается только на определённых маршрутах:

  • /map2d — без Cesium
  • /map3d — загрузка Cesium chunk
  • /analysis — частичная загрузка Cesium modules

Маршрутная стратегия позволяет полностью исключить Cesium из критического пути загрузки.

Изоляция тяжёлых зависимостей Cesium

Cesium включает математические библиотеки, декодеры и графические утилиты. При неаккуратной сборке они попадают в общий bundle.

Выделение зависимостей:

  • geometry engine
  • shader utilities
  • tile parsing
  • coordinate transforms

Каждый слой может быть вынесен в отдельный динамический импорт или отдельный бандл, что уменьшает размер основного entrypoint.

Асинхронная инициализация Viewer как точка разделения

Viewer является центральным объектом CesiumJS и часто становится границей разделения кода:

export async function initViewer(container) {
  const cesium = await import("cesium");

  const viewer = new cesium.Viewer(container, {
    animation: false,
    timeline: false,
  });

  return viewer;
}

Отделение Viewer от bootstrap-логики приложения позволяет полностью контролировать момент загрузки тяжёлых зависимостей.

Управление кэшированием чанков Cesium

Code splitting эффективно только при корректной стратегии кэширования:

  • стабильные имена чанков для Cesium
  • долгосрочное кэширование static assets
  • versioned CDN paths

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

Пограничные случаи и скрытые зависимости

Некоторые модули Cesium имеют транзитивные зависимости, которые могут неожиданно попасть в основной bundle:

  • Ellipsoid math utilities
  • default shader chunks
  • entity collection internals
  • geometry pipeline helpers

Контроль этих зависимостей требует анализа графа модулей и корректного разделения entrypoints на уровне сборщика.

Итоговая архитектурная модель разделения

Типовая схема code splitting в CesiumJS-приложении включает:

  • минимальный bootstrap bundle
  • UI bundle
  • Cesium core chunk
  • Cesium workers chunk
  • feature-based chunks (3D, analysis, tools)
  • lazy-loaded data providers
  • route-based dynamic imports