Документирование кода является важной частью разработки картографических приложений на Mapbox GL JS. По мере роста проекта увеличивается количество источников данных, слоёв, обработчиков событий, пользовательских компонентов и интеграций со сторонними сервисами. Отсутствие качественной документации приводит к усложнению сопровождения, увеличению времени внедрения новых разработчиков и повышению вероятности ошибок.
Документация должна объяснять не только то, что делает код, но и почему выбрано именно такое решение. Особенно это актуально для сложных картографических интерфейсов, где логика отображения данных может быть тесно связана с бизнес-процессами.
Документирование обычно разделяется на несколько уровней:
Описывает архитектуру приложения:
Пример:
src/
├── map/
│ ├── layers/
│ ├── sources/
│ └── controls/
├── services/
├── utils/
└── styles/
Такое описание помогает быстро понять организацию проекта.
Каждый модуль должен иметь описание назначения.
Пример файла:
// layers/buildings.js
/**
* Создание слоя трехмерных зданий.
* Используется после полной загрузки стиля карты.
*/
export function addBuildingsLayer(map) {
// ...
}
Без подобного комментария назначение файла может быть неочевидным спустя несколько месяцев после разработки.
Наиболее распространённый уровень документирования.
Пример:
/**
* Добавляет источник GeoJSON на карту.
*
* @param {mapboxgl.Map} map Экземпляр карты.
* @param {string} sourceId Идентификатор источника.
* @param {Object} data GeoJSON объект.
*/
function addGeoJsonSource(map, sourceId, data) {
map.addSource(sourceId, {
type: 'geojson',
data
});
}
Документация позволяет быстро определить:
Для JavaScript-проектов наиболее популярным форматом документирования является JSDoc.
Mapbox GL JS активно применяется в крупных проектах, поэтому использование JSDoc значительно упрощает поддержку кода.
/**
* Создает слой маркеров.
*/
function createMarkersLayer() {
}
/**
* Перемещает карту в указанную точку.
*
* @param {[number, number]} coordinates Координаты [lng, lat].
* @param {number} zoom Уровень масштабирования.
*/
function flyToLocation(coordinates, zoom) {
map.flyTo({
center: coordinates,
zoom
});
}
/**
* Возвращает центр карты.
*
* @returns {[number, number]}
*/
function getMapCenter() {
const center = map.getCenter();
return [center.lng, center.lat];
}
/**
* @typedef {Object} LayerConfig
* @property {string} id Идентификатор слоя.
* @property {string} source Источник данных.
* @property {string} color Цвет объектов.
*/
Использование типов особенно полезно при работе с многочисленными настройками слоёв.
Инициализация карты является ключевой частью приложения и требует подробного описания.
Пример:
/**
* Создает экземпляр карты.
*
* @param {string} containerId ID HTML-контейнера.
* @returns {mapboxgl.Map}
*/
function createMap(containerId) {
return new mapboxgl.Map({
container: containerId,
style: 'mapbox://styles/mapbox/streets-v12',
center: [37.6173, 55.7558],
zoom: 10
});
}
При необходимости можно дополнительно пояснить причины выбора конкретного стиля или стартового масштаба.
Источники данных являются фундаментом любого картографического приложения.
Пример:
/**
* Добавляет источник данных с объектами недвижимости.
*
* Источник обновляется каждые 5 минут через API.
*/
map.addSource('properties', {
type: 'geojson',
data: propertiesData
});
Подобные комментарии помогают понять происхождение данных.
Особенно важно документировать пользовательские свойства объектов.
/**
* Структура объекта недвижимости:
*
* {
* "type": "Feature",
* "properties": {
* "id": Number,
* "price": Number,
* "status": String
* }
* }
*/
Без таких комментариев работа с большими наборами геоданных быстро становится затруднительной.
Карта может содержать десятки слоёв.
Каждый слой должен иметь описание назначения.
Пример:
/**
* Слой отображения автомобильных дорог.
*/
map.addLayer({
id: 'roads',
type: 'line',
source: 'roads-source'
});
Часто порядок добавления слоёв имеет критическое значение.
/**
* Добавляется после слоя дорог,
* чтобы подписи отображались поверх линий.
*/
map.addLayer({
id: 'road-labels',
type: 'symbol',
source: 'roads-source'
});
Такой комментарий предотвращает ошибки при рефакторинге.
Mapbox GL JS активно использует Expressions.
Сложные выражения желательно сопровождать пояснениями.
Плохой пример:
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
5, 2,
15, 20
]
Хороший пример:
/**
* Радиус маркера растет по мере увеличения масштаба:
* zoom 5 -> radius 2
* zoom 15 -> radius 20
*/
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
5, 2,
15, 20
]
События составляют значительную часть логики Mapbox-приложений.
Пример:
/**
* Отображение карточки объекта при клике.
*/
map.on('click', 'buildings-layer', (event) => {
const feature = event.features[0];
showBuildingInfo(feature);
});
/**
* При наведении:
* 1. Изменяется курсор.
* 2. Подсвечивается объект.
* 3. Показывается всплывающая подсказка.
*/
map.on('mouseenter', 'restaurants-layer', () => {
map.getCanvas().style.cursor = 'pointer';
});
Комментарий позволяет быстро понять полный сценарий взаимодействия.
Во многих проектах создаются собственные элементы управления картой.
Пример:
/**
* Контрол переключения тематических слоев.
*/
class LayerSwitcherControl {
onAdd(map) {
this.map = map;
}
onRemove() {
}
}
/**
* Включает слой.
*
* @param {string} layerId
*/
enableLayer(layerId) {
}
Каждый публичный метод должен иметь описание поведения.
Картографические приложения часто получают данные через API.
Пример:
/**
* Загружает объекты с сервера
* и обновляет источник карты.
*/
async function loadFeatures() {
const response = await fetch('/api/features');
const data = await response.json();
map.getSource('features').setData(data);
}
/**
* Возможные ошибки:
* - недоступность сервера;
* - некорректный GeoJSON;
* - отсутствие источника данных.
*/
Такие пояснения облегчают сопровождение системы.
Для крупных приложений рекомендуется формализовать структуры данных.
Пример:
/**
* @typedef {Object} RestaurantFeature
*
* @property {number} id
* @property {string} name
* @property {string} category
* @property {number} rating
*/
После этого тип можно использовать в комментариях:
/**
* @param {RestaurantFeature} restaurant
*/
function createPopupContent(restaurant) {
}
Настройки приложения желательно выносить в отдельные объекты и документировать.
Пример:
/**
* Конфигурация карты.
*/
const MAP_CONFIG = {
center: [37.6173, 55.7558],
zoom: 10,
maxZoom: 18,
minZoom: 3
};
/**
* Максимальный масштаб ограничен значением 18,
* поскольку более высокие уровни недоступны
* в используемом наборе тайлов.
*/
Важно объяснять причины ограничений.
Стилизация слоёв часто становится сложной частью проекта.
Пример:
/**
* Цвет определяется уровнем загруженности дорог:
*
* green - свободно
* yellow - средняя загрузка
* red - пробка
*/
После этого следует соответствующее выражение Mapbox.
Комментарии должны фиксировать важные проектные решения.
Пример:
/**
* Используется единый GeoJSON-источник,
* поскольку обновление нескольких источников
* значительно увеличивает нагрузку на браузер.
*/
Такая информация имеет большую ценность, чем описание очевидных строк кода.
Плохой пример:
// Создаем карту
const map = new mapboxgl.Map();
Комментарий не добавляет новой информации.
Плохой пример:
/**
* Загружает XML данные.
*/
Фактический код:
fetch('/api/data.json');
Несоответствие документации опаснее полного отсутствия комментариев.
Плохой пример:
// Получаем центр карты
const center = map.getCenter();
// Выводим центр
console.log(center);
Очевидные действия не требуют пояснений.
JSDoc позволяет автоматически формировать документацию проекта.
Пример документированного класса:
/**
* Управляет отображением пользовательских маркеров.
*/
class MarkerManager {
/**
* Создает новый менеджер маркеров.
*
* @param {mapboxgl.Map} map
*/
constructor(map) {
this.map = map;
}
/**
* Добавляет маркер.
*
* @param {[number, number]} coordinates
*/
addMarker(coordinates) {
}
/**
* Удаляет все маркеры.
*/
clear() {
}
}
На основе таких комментариев можно автоматически получить полноценную справочную документацию.
Наиболее полезный комментарий:
/**
* Используется кластеризация,
* поскольку набор данных содержит
* более 50 000 объектов.
*/
Обязательному описанию подлежат:
Любое изменение:
должно сопровождаться обновлением соответствующей документации.
Пример единообразного оформления:
/**
* Краткое описание.
*
* @param {Type} param Описание параметра.
* @returns {Type} Описание результата.
*/
Единый формат делает проект значительно более понятным и облегчает навигацию по коду даже при большом количестве картографических компонентов, источников данных и пользовательских модулей, характерных для приложений на Mapbox GL JS.