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

Обычное подключение

<!DOCTYPE html>
<html>
    <head>
        <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
        <script src="index.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
async function initMap() {
    await ymaps3.ready;

    const {YMap, YMapDefaultSchemeLayer} = ymaps3;

    const map = new YMap(
        document.getElementById('app'),
        {
            location: {
                center: [37.588144, 55.733842],
                zoom: 10
            }
        }
    );

    map.addChild(new YMapDefaultSchemeLayer());
}

initMap();
{
    "devDependencies": {
        "http-server": "14.1.1"
    },
    "scripts": {
        "start": "npx http-server ."
    }
}

Поставьте зависимости и запустите локальный сервер:

npm install
npm run start

Откройте приложение

Внимание

Работа API гарантируется в браузерах, чья аудитория больше 0.5% по данным Browserslist.

Особенности обычного подключения

  1. JS API распространяется исключительно по ссылке, которую нужно подключать в шапке документа.
  2. Компоненты API всегда загружаются асинхронно.
  3. Компоненты существуют только в глобальной области видимости в переменной ymaps3.
  4. Компоненты доступны только после разрешения промиса ymaps3.ready.

Параметры загрузки API

apikey

Обязательный параметр

Ключ, полученный в Кабинете Разработчика.

Примечание

Ключ будет активирован в течение 15 минут после получения.

JS API 3.0 работает только с ключами, у которых заполнено поле "Ограничение по HTTP Referer". Подробнее об ограничениях

lang

Обязательный параметр

Локаль, задается в виде lang=language_region:

Промис ready

Промис ymaps3.ready гарантирует, что все компоненты основного модуля Javasript API загружены, а DOM построен.

Внимание

Обработчики событий типа document.ready, window.onload, jQuery.ready не сигнализируют об окончании загрузки компонентов JS API.

Подключение через top-level-await (рекомендуется)

<!DOCTYPE html>
<html>
  <head>
    <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
    <script type="module" src="index.js"></script>
  </head>
  <body>
    <div id="app" style="width: 600px; height: 400px"></div>
  </body>
</html>
import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js'

const map = new YMap(
    document.getElementById('app'),
    {
        location: {
              center: [37.588144, 55.733842],
              zoom: 10
        }
    }
);

map.addChild(new YMapDefaultSchemeLayer());
await ymaps3.ready;

export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
{
  "devDependencies": {
    "http-server": "14.1.1"
  },
  "scripts": {
    "start": "npx http-server ."
  }
}

Поставьте зависимости и запустите локальный сервер:

npm install
npm run start

Откройте приложение

Особенности

  1. В теге script, который загружает скомпилированный проектный js, указываем атрибут type="module", чтобы активировать поддержку ECMAScript Modules (ESM) и top-level-await:

    <script type="module" src="index.js"></script>
    
    Поддержка ESM при использовании бандлеров

    Если для сборки проекта используется бандлер, например Webpack, то в файл package.json необходимо добавить "type": "module"

  2. В файле lib/ymaps.js дожидаемся полной загрузки JS API, после чего экспортируем необходимые компоненты карты для их использования в других частях проекта:

    await ymaps3.ready;
    
    export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
    
  3. Использование top-level-await в lib/ymaps.js гарантирует выполнение ymaps3.ready до импорта компонентов карты, поэтому проектный код можно писать короче и опрятнее (больше не нужна асинхронная функция initMap):

    import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js'
    
    const map = new YMap({...});
    

Подключение с Webpack

  • Можно ли не подключать JS API в шапке HTML-документа?
  • Можно ли вызывать компоненты JS API не из глобальной переменной, а более привычно, словно они поставляются через npm-пакет?
  • Можно ли сделать так, чтобы не приходилось обрабатывать разрешение промиса ymaps3.ready?

С помощью Webpack и его опции externals это сделать можно. Есть три способа, каждый имеет как преимущества, так и ограничения.

Способ №1

<!DOCTYPE html>
<html>
    <head>
        <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

async function initMap() {
  await ymaps3.ready;

  const {YMap, YMapDefaultSchemeLayer} = ymaps3;

  const map = new YMap(
      document.getElementById('app'),
      {
          location: {
              center: [37.588144, 55.733842],
              zoom: 10
          }
      }
  );

  map.addChild(new YMapDefaultSchemeLayer());
}

initMap();
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externals: {
        ymaps3: 'ymaps3'
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Поставьте зависимости, соберите проект и запустите локальный сервер:

npm install
npm run build
npm run start

Откройте приложение

Особенности

  1. На HTML-странице всё ещё подключаем загрузчик JS API:

    <head>
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
    </head>
    

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

  2. В webpack.config.js объявляем внешнюю переменную ymaps3:

    module.exports = {
        // ...
        externals: {
            ymaps3: 'ymaps3'
        }
    };
    

    Благодаря этому в проектном коде появляется возможность импортировать ymaps3 так, словно код ymaps3 поставляется не через глобальную переменную, а через npm-пакет:

    import * as ymaps3 from 'ymaps3';
    // ...
    

Способ №2

<!DOCTYPE html>
<html>
    <head>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

async function initMap() {
  await ymaps3.ready;

  const {YMap, YMapDefaultSchemeLayer} = ymaps3;

  const map = new YMap(
      document.getElementById('app'),
      {
          location: {
              center: [37.588144, 55.733842],
              zoom: 10
          }
      }
  );

  map.addChild(new YMapDefaultSchemeLayer());
}

initMap();

const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externalsType: 'script',
    externals: {
        // Вместо YOUR_API_KEY подставить значение настоящего ключа
        ymaps3: ['https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU', 'ymaps3']
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Поставьте зависимости, соберите проект и запустите локальный сервер:

npm install
npm run build
npm run start

Откройте приложение

Особенности

Это видоизмененный первый способ:

  1. Больше не нужно в шапке HTML-страницы подключать тег <script>. Этим занимается сам webpack.

  2. В webpack.config.js так же объявляется внешняя переменная ymaps3, но в ней указывается путь к загрузчику API:

    module.exports = {
        // ...
        externalsType: 'script',
        externals: {
            ymaps3: ['https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU', 'ymaps3']
        }
    };
    

Ограничения способа №1 и №2

В обоих вышеописанных способах импортируется не готовое API, а легковесный загрузчик. Компоненты становятся полностью доступны только после разрешения промиса ymaps3.ready. Это означает, что если ваш клиентский код разбит на свои модули, и JS API используется сразу в нескольких ваших модулях, следует в каждом вашем модуле дожидаться разрешения промиса ymaps3.ready.

Например, вы хотите отобразить на странице сразу две карты, каждую в своем контейнере. Вам удобнее описывать эту функциональность не в одном большом модуле, а в двух разных, например с именами module-a.js и module-b.js. Тогда с использованием вышеописанных способов код ваших модулей будет выглядеть примерно так:

// Файл module-a.js
import * as ymaps3 from 'ymaps3';

async function initMap() {
    // Дождитесь резолва`ymaps3.ready`
    await ymaps3.ready;

    const {YMap} = ymaps3;

    // Карта инициализируется в первом контейнере
    const map = new YMap({document.getElementById('first-map-container')});
}

initMap();
// Файл module-b.js
import * as ymaps3 from 'ymaps3';

async function initMap() {
    // Дождитесь резолва`ymaps3.ready`
    await ymaps3.ready;

    const {YMap} = ymaps3;

    // Карта инициализируется во втором контейнере
    const map = new YMap({document.getElementById('second-map-container')});
}

initMap();

Приходится в каждом модуле дожидаться разрешения ymaps3.ready. Это неудобно, хотелось бы писать код проще:

// Файл module-a.js
import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({document.getElementById('first-map-container')});
// Файл module-b.js
import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({document.getElementById('second-map-container')});

Чтобы этого достичь, воспользуйтесь третьим способом.

Способ №3

<!DOCTYPE html>
<html>
    <head>
        <script src="build/bundle.js"></script>
    </head>
    <body>
        <div id="app" style="width: 600px; height: 400px"></div>
    </body>
</html>
import * as ymaps3 from 'ymaps3';

const {YMap, YMapDefaultSchemeLayer} = ymaps3;

const map = new YMap(
  document.getElementById('app'),
  {
      location: {
          center: [37.588144, 55.733842],
          zoom: 10
      }
  }
);

map.addChild(new YMapDefaultSchemeLayer());
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externalsType: 'script',
    externals: {
        // Вместо YOUR_API_KEY подставить значение настоящего ключа
        ymaps3: [
          `promise new Promise((resolve) => {
              if (typeof ymaps3 !== 'undefined') {
                return ymaps3.ready.then(() => resolve(ymaps3));
              }
              const script = document.createElement('script');
              script.src = "https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU";
              script.onload = () => {
                ymaps3.ready.then(() => resolve(ymaps3));
              };
              document.head.appendChild(script);
            })`
        ]
    },
    devtool: 'cheap-source-map'
};
{
    "devDependencies": {
        "http-server": "14.1.1",
        "webpack": "5.88.2",
        "webpack-cli": "5.1.4"
    },
    "scripts": {
        "build": "webpack",
        "start": "npx http-server ."
    }
}

Поставьте зависимости, соберите проект и запустите локальный сервер:

npm install
npm run build
npm run start

Откройте приложение

Особенности

Это почти тот же способ №2, только в webpack.config.js при объявлении переменной ymaps3 несколько иначе указывается путь к загрузчику API и там же происходит разрешение промиса ymaps3.ready.

При таком подключении в собранном проектном js-файле становятся доступны полностью загруженные модули JS API и гарантируется исполнение ymaps3.ready, поэтому проектный код можно писать короче и опрятнее:

import * as ymaps3 from 'ymaps3';

const {YMap} = ymaps3;
const map = new YMap({...});

Внимание

При таком способе подключения проектный код не начнет выполняться до тех пор, пока компоненты JS API не будут полностью загружены.

Ограничение способа №3

Например, вы хотите показывать анимацию загрузки до момента, пока не загрузились компоненты карты. Подключили JS API способом №3 и написали для этого вот такой код. К сожалению, у вас не получится выполнить задачу:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ymaps3 from 'ymaps3';

// Выполнение этой строки начнется только после загрузки ymaps3 и резолва ymaps3.ready
const reactify = ymaps3.reactify.bindTo(React, ReactDOM);
const {YMap} = reactify.module(ymaps3);

function MapView() {
    const [loading, setLoading] = React.useState(true);

    React.useEffect(() => {
        setLoading(false);
    }, []);

    // Этот код бесполезен. Он, конечно, выполнится при монтировании компонента в DOM, но
    // монтирование-то произойдет лишь после загрузки ymaps3 и резолва ymaps3.ready.
    // Поэтому при монтировании пользователь на долю секунды увидит статус 'Loading...',
    // а потом сразу же случится useEffect, который отменит показ анимации загрузки.
    if (loading) {
        return <div>Loading...</div>;
    }

    return <YMap />;
}

function App() {
    return (
        <MapView />
    );
}

Главный недостаток webpack-подключения №3 состоит в том, что в проектных модулях не будет ничего выполняться до тех пока, пока компоненты JS API не загрузятся полностью.

Чтобы показать анимацию загрузки, от способа №3 придётся отказаться:

  1. Перейти на webpack-подключение №1 или №2.
  2. Внутри React.useEffect нужно дождаться ymaps3.ready и только после этого снять флажок loading.
  3. Код, использующий reactify-модуль, необходимо поместить внутрь компонента MapView так, чтобы JS-интерпретатор смог "прочитать" эти строчки только после разрешения ymaps3.ready:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ymaps3 from 'ymaps3';

function MapView() {
    const [loading, setLoading] = React.useState(true);

    React.useEffect(() => {
        ymaps3.ready.then(() => setLoading(false));
    }, []);

    if (loading) {
        return <div>Loading...</div>;
    }

    const reactify = ymaps3.reactify.bindTo(React, ReactDOM);
    const {YMap} = reactify.module(ymaps3);

    return <YMap />;
}

function App() {
    return (
        <MapView />
    );
}

Переключение языка

Помните, что при подключении API указывается язык. Если вы создаете интернациональные сайты, при любом способе подключения следите за его переключением.

Например, для подключения с Webpack третьим способом это можно сделать так:

module.exports = {
  //...
  externals: {
    ymaps3: [
      `promise new Promise((resolve) => {
          ...
          const lang = ['ru', 'ru-Ru'].includes(navigator.language) ? 'ru-Ru' : 'en-US';
          script.src = "https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=" + lang;
          ...
        })`
    ]
  }
};

Подключение пакетов и модулей

JS API предоставляет дополнительные пакеты и модули, полезные для решения специфических задач. Для их загрузки предусмотрен метод ymaps3.import. Подробнее читайте в документации пакетов и модулей