Polyfills для старых браузеров

Mapbox GL JS активно использует современные возможности JavaScript и Web API. Большинство современных браузеров поддерживают необходимые функции из коробки, однако при работе со старыми версиями браузеров возникают проблемы совместимости. В таких случаях используются polyfills — специальные библиотеки, реализующие отсутствующие возможности среды выполнения.

Polyfill представляет собой программную реализацию стандартного API, которая добавляет поддержку новых возможностей JavaScript в браузеры, где эти возможности отсутствуют.

Для Mapbox GL JS polyfills особенно важны при поддержке:

  • старых версий Internet Explorer;
  • ранних версий Microsoft Edge;
  • устаревших мобильных браузеров Android;
  • старых версий Safari;
  • встроенных браузеров различных устройств.

Причины возникновения проблем совместимости

Современные версии Mapbox GL JS используют:

  • Promises;
  • Fetch API;
  • Object.assign();
  • Array.prototype.includes();
  • URL API;
  • requestAnimationFrame();
  • Typed Arrays;
  • Web Workers;
  • современные возможности ECMAScript.

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

Типичные сообщения об ошибках:

Promise is undefined
Object doesn't support property or method 'assign'
Array.prototype.includes is undefined
fetch is not defined

В подобных ситуациях polyfills позволяют сохранить работоспособность приложения без изменения бизнес-логики.


Поддержка браузеров в разных версиях Mapbox GL JS

Подход к совместимости зависит от версии библиотеки.

Ранние версии

Версии Mapbox GL JS 0.x и 1.x поддерживали более широкий набор браузеров, включая Internet Explorer 11.

Пример:

Mapbox GL JS 1.x
├── Chrome
├── Firefox
├── Safari
├── Edge Legacy
└── IE11

Современные версии

Начиная с новых поколений библиотеки поддержка устаревших браузеров значительно сократилась.

Причины:

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

Поэтому при разработке корпоративных приложений с требованиями поддержки старых браузеров необходимо отдельно анализировать совместимость выбранной версии Mapbox GL JS.


Основные категории polyfills

Polyfills для ECMAScript

Добавляют отсутствующие возможности языка JavaScript.

Наиболее востребованные:

Promise
Map
Set
WeakMap
Object.assign
Array.includes
Array.from

Polyfills для браузерных API

Обеспечивают поддержку Web API.

Примеры:

fetch
URL
requestAnimationFrame

Polyfills для DOM API

Используются при работе с современными возможностями DOM.

Примеры:

Element.closest()
Element.matches()
CustomEvent

Использование core-js

Одним из самых популярных наборов polyfills является библиотека core-js.

Установка:

npm install core-js

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

import "core-js/stable";

Либо выборочное подключение:

import "core-js/es/promise";
import "core-js/es/object/assign";
import "core-js/es/array/includes";

После загрузки polyfills Mapbox GL JS получает доступ к отсутствующим возможностям среды выполнения.


Polyfill для Promise

Mapbox GL JS активно использует асинхронное программирование.

Старые браузеры не поддерживают объект Promise.

Установка:

npm install es6-promise

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

import { polyfill } from "es6-promise";

polyfill();

После инициализации становится доступен стандартный API:

Promise.resolve("Mapbox");

Polyfill для Fetch API

Запросы к стилям, тайлам и геоданным часто используют Fetch API.

Установка:

npm install whatwg-fetch

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

import "whatwg-fetch";

После подключения появляется глобальная функция:

fetch("/data.json")
    .then(response => response.json())
    .then(data => console.log(data));

Для старых браузеров это особенно важно, поскольку отсутствие fetch вызывает критическую ошибку загрузки ресурсов.


Polyfill для Object.assign

Mapbox GL JS использует объединение объектов в различных внутренних механизмах.

Подключение через core-js:

import "core-js/es/object/assign";

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

const result = Object.assign(
    {},
    defaults,
    options
);

В старых браузерах без polyfill данный код завершится ошибкой.


Polyfill для Array.prototype.includes

Многие современные библиотеки используют метод includes().

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

import "core-js/es/array/includes";

Пример:

const layers = ["roads", "water"];

layers.includes("roads");

Без polyfill Internet Explorer не сможет выполнить подобный код.


Polyfill для URL API

Некоторые механизмы работы с адресами используют объект URL.

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

npm install url-polyfill

Импорт:

import "url-polyfill";

Пример:

const url = new URL(
    "https://example.com/map?zoom=5"
);

console.log(url.searchParams.get("zoom"));

Использование Babel совместно с polyfills

Часто polyfills применяются совместно с транспиляцией кода через Babel.

Установка:

npm install \
@babel/core \
@babel/preset-env \
babel-loader

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

module.exports = {
    presets: [
        [
            "@babel/preset-env",
            {
                useBuiltIns: "usage",
                corejs: 3
            }
        ]
    ]
};

В этом режиме Babel автоматически анализирует код и добавляет только необходимые polyfills.

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

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

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

Для корректной работы Babel обычно используется Browserslist.

Пример:

{
  "browserslist": [
    ">0.5%",
    "last 2 versions",
    "not dead",
    "IE 11"
  ]
}

На основе этих настроек Babel определяет набор необходимых polyfills.


Polyfills через CDN

Если проект не использует сборщик модулей, polyfills можно подключать через CDN.

Пример:

<script src="https://polyfill.io/v3/polyfill.min.js"></script>

После загрузки можно подключать Mapbox GL JS:

<script src="mapbox-gl.js"></script>

Такой подход часто встречается в небольших проектах и прототипах.


Порядок подключения

Polyfills должны загружаться раньше Mapbox GL JS.

Правильный порядок:

<script src="polyfills.js"></script>

<script src="mapbox-gl.js"></script>

<script src="app.js"></script>

Неправильный порядок:

<script src="mapbox-gl.js"></script>

<script src="polyfills.js"></script>

Во втором случае библиотека может завершить инициализацию с ошибкой ещё до загрузки polyfills.


Проверка поддержки возможностей

Иногда требуется определить необходимость загрузки polyfills во время выполнения.

Пример:

if (!window.Promise) {
    loadPromisePolyfill();
}

Проверка Fetch API:

if (!window.fetch) {
    loadFetchPolyfill();
}

Проверка Object.assign:

if (!Object.assign) {
    loadAssignPolyfill();
}

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


Условная загрузка polyfills

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

Пример:

async function prepareBrowser() {
    if (!window.fetch) {
        await import("whatwg-fetch");
    }

    if (!window.Promise) {
        await import("es6-promise");
    }
}

После подготовки среды:

prepareBrowser()
    .then(() => initializeMap());

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


Web Workers и ограничения polyfills

Mapbox GL JS активно использует Web Workers для обработки данных карты.

Важно понимать, что polyfills способны компенсировать отсутствие многих возможностей JavaScript, однако не могут полностью реализовать некоторые низкоуровневые браузерные механизмы.

Например:

Promise           → polyfill возможен
Object.assign     → polyfill возможен
Fetch API         → polyfill возможен
Web Workers       → полноценная замена невозможна
WebGL             → полноценная замена невозможна

Если браузер не поддерживает WebGL, карта Mapbox GL JS работать не сможет независимо от количества подключённых polyfills.


Проверка поддержки WebGL

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

Пример:

if (!mapboxgl.supported()) {
    alert("WebGL не поддерживается");
}

Метод:

mapboxgl.supported()

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


Типичные ошибки при использовании polyfills

Избыточное подключение

Ошибка:

import "core-js/stable";
import "core-js/full";

В результате значительно увеличивается размер бандла.


Подключение после Mapbox GL JS

Ошибка:

<script src="mapbox-gl.js"></script>
<script src="polyfills.js"></script>

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


Несовместимые версии

Некоторые старые polyfills могут конфликтовать с современными сборщиками.

Рекомендуется использовать актуальные версии:

core-js
whatwg-fetch
es6-promise
url-polyfill

Отсутствие тестирования

Наличие polyfill не гарантирует полную работоспособность приложения.

Необходимо тестировать:

  • загрузку стилей;
  • отображение тайлов;
  • работу источников данных;
  • обработку событий;
  • производительность карты;
  • работу WebGL.

Рекомендуемая схема поддержки старых браузеров

Для проектов, где требуется максимальная совместимость с устаревшими браузерами, обычно применяется следующий набор:

import "core-js/stable";
import "regenerator-runtime/runtime";
import "whatwg-fetch";
import "url-polyfill";

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

{
    presets: [
        [
            "@babel/preset-env",
            {
                useBuiltIns: "usage",
                corejs: 3
            }
        ]
    ]
}

Проверка поддержки карты:

if (mapboxgl.supported()) {
    initializeMap();
}

Такая комбинация обеспечивает поддержку большинства возможностей JavaScript, необходимых для работы Mapbox GL JS в устаревших браузерах, сохраняя при этом совместимость с современными инструментами сборки и разработки.