Настройка webpack и сборщиков

Deck.gl представляет собой высокопроизводительную библиотеку визуализации геоданных и аналитических данных на основе WebGL. Современные версии библиотеки распространяются через npm-пакеты и ориентированы на использование вместе с системами сборки JavaScript-приложений. Корректная настройка сборщика напрямую влияет на размер итогового бандла, скорость загрузки приложения, производительность разработки и возможность использовать современные возможности языка.

В большинстве проектов Deck.gl используется совместно с:

  • React;
  • TypeScript;
  • MapLibre;
  • Mapbox;
  • loaders.gl;
  • luma.gl;
  • Babel;
  • Webpack;
  • Vite;
  • Rollup;
  • Parcel.

Несмотря на появление современных инструментов сборки, webpack остаётся одним из наиболее распространённых решений в корпоративных и долгоживущих проектах.


Установка зависимостей

Минимальный набор пакетов для использования Deck.gl с webpack выглядит следующим образом:

npm install deck.gl
npm install react react-dom
npm install webpack webpack-cli webpack-dev-server
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react

Для TypeScript дополнительно устанавливаются:

npm install typescript ts-loader

или:

npm install typescript babel-loader @babel/preset-typescript

Структура проекта может выглядеть следующим образом:

project/
├── src/
│   ├── index.js
│   ├── App.jsx
│   └── layers/
├── public/
│   └── index.html
├── package.json
├── webpack.config.js
└── babel.config.json

Базовая конфигурация webpack

Простейший файл конфигурации:

const path = require('path');

module.exports = {
  mode: 'development',

  entry: './src/index.js',

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },

  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },

  resolve: {
    extensions: ['.js', '.jsx']
  },

  devServer: {
    static: './public',
    hot: true
  }
};

Данная конфигурация обеспечивает:

  • транспиляцию современного JavaScript;
  • поддержку JSX;
  • локальный сервер разработки;
  • автоматическую перезагрузку приложения.

Настройка Babel

Файл babel.config.json:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

После настройки Babel можно импортировать Deck.gl непосредственно в коде приложения:

import {DeckGL} from '@deck.gl/react';

Webpack обработает все зависимости и сформирует единый пакет для браузера.


Создание точки входа

Файл src/index.js:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root')
);

root.render(<App />);

Файл App.jsx:

import DeckGL from '@deck.gl/react';

export default function App() {
  return (
    <DeckGL
      initialViewState={{
        longitude: 37.6,
        latitude: 55.75,
        zoom: 10
      }}
      controller={true}
      layers={[]}
    />
  );
}

После сборки webpack сформирует единый бандл, содержащий React и Deck.gl.


Использование режима production

Во время разработки применяется режим:

mode: 'development'

Для выпуска приложения используется:

mode: 'production'

Production-режим автоматически включает:

  • минификацию кода;
  • tree shaking;
  • оптимизацию модулей;
  • удаление части отладочной информации.

Пример запуска:

webpack --mode production

или через npm:

{
  "scripts": {
    "build": "webpack --mode production"
  }
}

Tree Shaking в Deck.gl

Deck.gl имеет модульную архитектуру.

Неэффективный импорт:

import * as deck from 'deck.gl';

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

Предпочтительный вариант:

import {ScatterplotLayer} from '@deck.gl/layers';

или:

import {GeoJsonLayer} from '@deck.gl/layers';

Webpack способен исключать неиспользуемые модули при построении production-сборки.

Пример:

import {ScatterplotLayer} from '@deck.gl/layers';

const layer = new ScatterplotLayer({
  data
});

В итоговый бандл попадёт только необходимый код.


Разделение кода (Code Splitting)

Deck.gl может использоваться лишь на отдельных страницах приложения.

В таких случаях полезно применять динамические импорты.

Без разделения:

import {GeoJsonLayer} from '@deck.gl/layers';

С разделением:

const GeoJsonLayer = await import(
  '@deck.gl/layers'
);

или:

const module = await import('@deck.gl/layers');

const layer = new module.GeoJsonLayer({
  data
});

Webpack создаст отдельный chunk-файл.

Преимущества:

  • уменьшение первоначальной загрузки;
  • более быстрое открытие приложения;
  • экономия памяти браузера.

Настройка React Lazy

Для больших картографических интерфейсов часто применяется ленивое подключение компонентов.

import React, {lazy} from 'react';

const MapPage = lazy(() =>
  import('./MapPage')
);

Внутри MapPage могут располагаться тяжёлые слои Deck.gl.

import {TileLayer} from '@deck.gl/geo-layers';
import {MVTLayer} from '@deck.gl/geo-layers';

Загрузка произойдёт только при открытии соответствующего маршрута.


Работа с TypeScript

Конфигурация webpack для TypeScript:

module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },

  resolve: {
    extensions: [
      '.tsx',
      '.ts',
      '.js'
    ]
  }
};

Файл tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "jsx": "react-jsx",
    "strict": true
  }
}

Пример использования слоя:

import {ScatterplotLayer} from '@deck.gl/layers';

const layer = new ScatterplotLayer({
  id: 'points',
  data
});

TypeScript предоставляет типизацию конфигураций слоёв и параметров визуализации.


Работа с Web Worker

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

Webpack поддерживает подключение Worker-модулей:

const worker = new Worker(
  new URL('./worker.js', import.meta.url)
);

Конфигурация webpack 5 не требует дополнительных загрузчиков.

Пример обработки геоданных:

worker.postMessage(bigDataset);

Внутри воркера:

self.onmess age = event => {
  const processed = processData(event.data);

  self.postMessage(processed);
};

Deck.gl получает уже подготовленные данные без блокировки интерфейса.


Оптимизация размера бандла

Размер приложения можно анализировать при помощи специального плагина.

Установка:

npm install webpack-bundle-analyzer

Подключение:

const {
  BundleAnalyzerPlugin
} = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

После сборки открывается визуальная диаграмма зависимостей.

Чаще всего наибольший вклад в размер пакета вносят:

  • картографические библиотеки;
  • наборы геоданных;
  • GeoJSON-файлы;
  • библиотеки визуализации;
  • утилиты обработки пространственных данных.

Настройка алиасов

В больших проектах количество импортов быстро растёт.

Без алиасов:

import MapLayer from
'../. ./. ./. ./components/map/layers/MapLayer';

С алиасами:

resolve: {
  alias: {
    '@layers': path.resolve(
      __dirname,
      'src/layers'
    )
  }
}

Использование:

import MapLayer from '@layers/MapLayer';

Подобный подход значительно упрощает поддержку крупного проекта.


Работа с ресурсами

Слои Deck.gl часто используют внешние данные.

Например:

fetch('/data/cities.json')

Webpack позволяет автоматически копировать ресурсы.

Установка:

npm install copy-webpack-plugin

Настройка:

const CopyPlugin =
require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: 'public/data',
          to: 'data'
        }
      ]
    })
  ]
};

После сборки JSON-файлы будут доступны приложению.


Использование переменных окружения

Для разных окружений обычно применяются различные источники данных.

Установка:

npm install dotenv-webpack

Конфигурация:

const Dotenv = require('dotenv-webpack');

module.exports = {
  plugins: [
    new Dotenv()
  ]
};

Файл .env:

API_URL=https://api.example.com

Использование:

const url =
process.env.API_URL;

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


Source Maps

При отладке сложных визуализаций важно видеть оригинальный код.

Настройка:

module.exports = {
  devtool: 'source-map'
};

Варианты:

devtool: 'eval-source-map'

или:

devtool: 'cheap-module-source-map'

Преимущества:

  • корректные номера строк;
  • удобная диагностика ошибок;
  • упрощённая отладка пользовательских слоёв.

Настройка кеширования

Для production-сборок рекомендуется использовать хэширование файлов.

output: {
  filename: '[name].[contenthash].js'
}

Пример результата:

main.8f5d13a2.js
vendors.a7827c3e.js

Браузер будет повторно загружать только изменённые файлы.


Выделение vendor-бандла

Deck.gl и React редко изменяются по сравнению с бизнес-логикой приложения.

Поэтому полезно выделять зависимости в отдельный пакет.

optimization: {
  splitChunks: {
    chunks: 'all'
  }
}

Webpack автоматически сформирует отдельные файлы:

vendors.js
main.js

Преимущества:

  • лучшее кеширование;
  • ускорение повторных посещений;
  • уменьшение объёма обновлений.

Использование webpack-dev-server

Для разработки картографических приложений важна быстрая обратная связь.

Конфигурация:

devServer: {
  port: 3000,
  hot: true,
  open: true
}

Запуск:

npx webpack serve

Возможности:

  • горячая перезагрузка модулей;
  • автоматическое обновление интерфейса;
  • мгновенное отображение изменений в слоях Deck.gl.

Типовая production-конфигурация

const path = require('path');

module.exports = {
  mode: 'production',

  entry: './src/index.js',

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true
  },

  resolve: {
    extensions: ['.js', '.jsx']
  },

  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },

  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

Подобная конфигурация обеспечивает эффективную работу приложений на базе Deck.gl, поддерживает современные возможности JavaScript, уменьшает размер итогового бандла, ускоряет загрузку картографических интерфейсов и создаёт основу для масштабирования крупных проектов визуализации геоданных.