Code splitting — техника разделения JavaScript-приложения на несколько независимых частей (chunks), которые загружаются по мере необходимости. При использовании Kepler.gl данный подход особенно важен, поскольку библиотека включает значительный объём кода для визуализации геоданных, обработки слоёв, работы с картографическими движками и пользовательским интерфейсом.
В крупных приложениях загрузка всей функциональности Kepler.gl вместе с основным бандлом приводит к следующим проблемам:
Code splitting позволяет загружать Kepler.gl только тогда, когда пользователь действительно открывает карту или работает с геопространственными данными.
Архитектура Kepler.gl включает множество зависимостей:
Даже минимальная интеграция карты может добавить в бандл несколько мегабайт JavaScript-кода.
Типичный сценарий:
Без code splitting все пользователи будут загружать код Kepler.gl независимо от того, нужен он им или нет.
Современный JavaScript предоставляет оператор
import().
Обычный импорт:
import KeplerGl from 'kepler.gl';
Динамический импорт:
const KeplerGl = await import('kepler.gl');
В первом случае код включается в основной бандл.
Во втором случае сборщик создаёт отдельный chunk, который будет загружен только в момент выполнения импорта.
Наиболее распространённый способ подключения Kepler.gl в
React-приложениях основан на React.lazy.
// MapPage.jsx
import KeplerGl from 'kepler.gl';
export default function MapPage() {
return (
<KeplerGl
id="map"
width={window.innerWidth}
height={window.innerHeight}
/>
);
}
import React, { Suspense, lazy } from 'react';
const MapPage = lazy(() => import('./MapPage'));
function App() {
return (
<Suspense fallback={<div>Загрузка карты...</div>}>
<MapPage />
</Suspense>
);
}
После сборки компонент карты будет находиться в отдельном JavaScript-файле.
Один из наиболее эффективных подходов — загружать Kepler.gl только при переходе на соответствующий маршрут.
Пример для React Router:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const MapPage = lazy(() => import('./MapPage'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/map" element={<MapPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Пользователь, работающий только с дашбордом, не загружает код карты.
Webpack автоматически создаёт отдельные чанки для динамических импортов.
Однако иногда требуется явно задать имя чанка.
const MapPage = lazy(() =>
import(
/* webpackChunkName: "kepler-map" */
'./MapPage'
)
);
После сборки может появиться файл:
kepler-map.js
Такой подход упрощает анализ бандла и отладку.
В приложениях Next.js рекомендуется использовать функцию
dynamic.
import dynamic from 'next/dynamic';
const MapPage = dynamic(
() => import('../components/MapPage'),
{
loading: () => <p>Loading map...</p>
}
);
export default function Home() {
return <MapPage />;
}
Kepler.gl зависит от браузерных API:
window;document;Поэтому серверный рендеринг часто вызывает ошибки.
Для Next.js обычно используется:
const MapPage = dynamic(
() => import('../components/MapPage'),
{
ssr: false
}
);
Теперь компонент будет загружаться только на клиенте.
Не только интерфейс карты может быть вынесен в отдельный chunk.
Часто большие объёмы данных предварительно преобразуются перед отображением.
Пример:
async function loadGeoData() {
const parser = await import('./geoParser');
return parser.parse();
}
В результате модуль обработки данных не попадает в основной бандл.
Большие GeoJSON-файлы могут достигать десятков мегабайт.
Нежелательно загружать их сразу после открытия приложения.
Вместо этого данные можно получать по требованию.
async function loadDataset() {
const response = await fetch('/data/cities.geojson');
return response.json();
}
Загрузка начинается только после открытия карты.
Иногда необходимо сохранить преимущества code splitting и одновременно уменьшить задержку открытия карты.
Для этого применяется prefetch.
Webpack поддерживает специальную директиву:
const MapPage = lazy(() =>
import(
/* webpackPrefetch: true */
'./MapPage'
)
);
Браузер загрузит chunk в фоновом режиме, когда сеть будет свободна.
Пользователь увидит карту быстрее.
Эти механизмы решают разные задачи.
Загрузка начинается практически сразу.
<link
rel="preload"
href="/kepler-map.js"
as="script"
/>
Используется для критически важных ресурсов.
Загрузка выполняется позже.
<link
rel="prefetch"
href="/kepler-map.js"
/>
Подходит для вероятных будущих переходов.
Для Kepler.gl обычно предпочтителен именно prefetch.
После внедрения code splitting важно проверить результат.
Для Webpack используется Bundle Analyzer.
Установка:
npm install webpack-bundle-analyzer
Подключение:
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
Отчёт покажет:
Крупные приложения часто выделяют внешние библиотеки в отдельный vendor chunk.
Пример настройки Webpack:
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
Преимущества:
Kepler.gl использует Redux для хранения состояния карты.
При ленивой загрузке карты желательно аналогичным образом загружать связанные редьюсеры.
Пример динамического подключения:
const reducers = await import('./reducers');
store.replaceReducer(
reducers.default
);
Такой подход особенно полезен для больших корпоративных систем с множеством независимых модулей.
Некоторые функции используются редко:
Они также могут подключаться динамически.
async function exportMap() {
const exporter =
await import('./mapExporter');
exporter.createImage();
}
Пользователь загрузит код экспорта только при нажатии соответствующей кнопки.
Для проектов с Kepler.gl рекомендуется следующая структура:
src/
├── pages/
│ ├── Dashboard
│ ├── Reports
│ └── MapPage
│
├── map/
│ ├── components
│ ├── layers
│ ├── datasets
│ ├── reducers
│ └── services
Модуль map становится полностью автономным и может
загружаться отдельным chunk.
Неправильная конфигурация сборщика способна привести к ситуации, когда:
Это увеличивает размер загрузки и может вызывать ошибки.
Необходимо контролировать:
resolve: {
dedupe: [
'react',
'react-dom'
]
}
или использовать корректную настройку общих зависимостей в Webpack, Vite либо Rollup.
Vite использует Rollup для production-сборок.
Для выделения Kepler.gl в отдельный chunk применяется настройка:
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
kepler: [
'kepler.gl',
'@deck.gl/core',
'@deck.gl/layers'
]
}
}
}
}
};
В результате формируется отдельный файл:
kepler.[hash].js
Даже после внедрения code splitting карта может загружаться заметно дольше остальных страниц.
Для ускорения рекомендуется:
Неправильно:
import KeplerGl from 'kepler.gl';
Даже если компонент позже оборачивается в lazy,
библиотека уже окажется в основном бандле.
Неправильно:
import geoData from './data.json';
Файл попадёт в главный bundle.
Лучше:
const data =
await fetch('/data.json')
.then(r => r.json());
Без Suspense пользователь может увидеть пустой
экран.
Правильно:
<Suspense
fallback={<Spinner />}
>
<MapPage />
</Suspense>
Чрезмерное дробление приложения приводит к:
Code splitting должен разделять логические модули, а не каждый файл отдельно.
Эффективная организация загрузки обычно выглядит следующим образом:
Подобная архитектура позволяет значительно сократить размер первоначального бандла, ускорить загрузку интерфейса и сделать работу приложений с Kepler.gl более масштабируемой и производительной.