<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
    <title></title>
    <link rel="self" type="application/atom+xml" href="https://soal.red/ru/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://soal.red"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-02-06T00:00:00+00:00</updated>
    <id>https://soal.red/ru/atom.xml</id>
    <entry xml:lang="ru">
        <title>В начале Осени</title>
        <published>2026-02-06T00:00:00+00:00</published>
        <updated>2026-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Alex Soal
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://soal.red/ru/stories/cms-idea/"/>
        <id>https://soal.red/ru/stories/cms-idea/</id>
        
        <content type="html" xml:base="https://soal.red/ru/stories/cms-idea/">&lt;p&gt;В этой главе мы увидим основные идеи, стоящие за новой CMS
(кодовое имя «Осень»).
Из этого хаотичного наброска вырастет цельное представление о том, что мы
хотим реализовать. Основная идея — создать простую в обращении и обслуживании,
продуманную и отполированную систему в духе Ghost и Craft, но добавить
больше инструментов редактирования и представления контента. Это позволит
использовать CMS изданиям, требующим большего, чем просто блог.&lt;&#x2F;p&gt;
&lt;p&gt;Одновременно мы хотим избежать хаоса и тяжёлого наследия прошлого, которым
страдает WordPress. Вопрос кастомизации должен быть продуман с самого начала,
но конкретная форма требует дополнительного исследования.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sushchestvuiushchie-resheniia&quot;&gt;Существующие решения&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;wordpress&quot;&gt;WordPress&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;ghost&quot;&gt;Ghost&lt;&#x2F;h3&gt;
&lt;p&gt;Он классный, но в нём не хватает нескольких компонентов:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Публикации на нескольких языках&lt;&#x2F;li&gt;
&lt;li&gt;Шаблонов для разных видов публикаций, который могут потребоваться
онлайн-изданию (новости, интервью, эссе, репортажи, около-академические статьи)&lt;&#x2F;li&gt;
&lt;li&gt;Интеграции с соцсетями, не распространёнными на Западе&lt;&#x2F;li&gt;
&lt;li&gt;Интеграции с платёжными системами, не распространёнными на Западе&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;craft-cms&quot;&gt;Craft CMS&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;craftcms.com&#x2F;&quot;&gt;Craft CMS&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;publikatsiia-kliuchevoi-element&quot;&gt;Публикация, ключевой элемент&lt;&#x2F;h2&gt;
&lt;figure class=&quot;breakout&quot;&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;img src=&quot;publication.svg&quot; alt=&quot;Схема публикации&quot; &#x2F;&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;metadannye&quot;&gt;Метаданные&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Авторы.&lt;&#x2F;strong&gt; Нам нужна гибкая настройка авторов — их может быть несколько и они
могут отличаться между версиями на разных языках.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Переводчики.&lt;&#x2F;strong&gt; Указывать авторство перевода это хороший тон.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Иллюстраторы.&lt;&#x2F;strong&gt; Иногда иллюстрации могут быть не менее важной частью
публикации.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Редакторы.&lt;&#x2F;strong&gt; Если это большая статья, то она может быть результатом
коллективной работы и редакторы тоже могут быть указаны.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Тэги.&lt;&#x2F;strong&gt; Категоризация публикаций.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SEO&lt;&#x2F;strong&gt;. Заголовок, описание и микроформаты для поисковиков и социальных сетей.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Связи между публикациями.&lt;&#x2F;strong&gt; Публикации могут быть объединены в серии или
представлять собой онлайн-дискуссию.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Даты.&lt;&#x2F;strong&gt; Дата создания, последнего изменения и публикации.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Внешние связи&lt;&#x2F;strong&gt;. Если публикация была также опубликована в соцсетях и
других внешних источниках, мы должны сохранить эту связь, хотя бы в виде
ссылки.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dokument&quot;&gt;Документ&lt;&#x2F;h3&gt;
&lt;p&gt;Сам документ должен хранится в формате, который отвечает нескольким
требованиям:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Поддерживает совместное редактирование (&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;automerge.org&#x2F;&quot;&gt;Automerge&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Поддерживает шаблоны для вёрстки контента&lt;&#x2F;li&gt;
&lt;li&gt;Позволяет эффективно представить контент в виде HTML с дополнениями в виде
веб-компонентов&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;komponenty-sistemy&quot;&gt;Компоненты системы&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;redaktor-v-kotorom-khochetsia-pisat&quot;&gt;Редактор, в котором хочется писать&lt;&#x2F;h3&gt;
&lt;p&gt;Цель создать редактор, который будет использоваться напрямую, а не просто
«написал в другом месте и скопировал сюда».&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bazovoe-formatirovanie&quot;&gt;Базовое форматирование&lt;&#x2F;h4&gt;
&lt;p&gt;Редактор должен поддерживать базовое форматирование (&lt;em&gt;курсив&lt;&#x2F;em&gt;, &lt;strong&gt;полужирный&lt;&#x2F;strong&gt;,
таблицы и т.д.). Администратор должен иметь возможность настроить доступные
редактору возможности форматирования для сохранения общего стиля издания.
К примеру, отключить выделение разными цветами или ограничить палитру цветами
бренда.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;blochnoe-redaktirovanie&quot;&gt;Блочное редактирование&lt;&#x2F;h4&gt;
&lt;p&gt;Документ разбит на сегменты, которые инкапсулируют свой контент в универсальный
контейнер. Внутри может быть параграф текста, заголовок, изображение,
произвольный HTML и т.д.
&lt;details&gt;
  
    &lt;summary&gt;Виды сегментов&lt;&#x2F;summary&gt;
  
  &lt;ul&gt;
&lt;li&gt;Параграф&lt;&#x2F;li&gt;
&lt;li&gt;Разделитель&lt;&#x2F;li&gt;
&lt;li&gt;Закладка
&lt;em&gt;(Ссылка с изображением и предпросмотром, аналогичная preview в соцсетях)&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Разделение на публичную&#x2F;закрытыю часть&lt;&#x2F;li&gt;
&lt;li&gt;Call to Action&#x2F;Ad&lt;&#x2F;li&gt;
&lt;li&gt;Email content&lt;&#x2F;li&gt;
&lt;li&gt;Callout&#x2F;Hero&lt;&#x2F;li&gt;
&lt;li&gt;Header&lt;&#x2F;li&gt;
&lt;li&gt;Signup&lt;&#x2F;li&gt;
&lt;li&gt;Toggle&lt;&#x2F;li&gt;
&lt;li&gt;Video&lt;&#x2F;li&gt;
&lt;li&gt;Audio&lt;&#x2F;li&gt;
&lt;li&gt;File&lt;&#x2F;li&gt;
&lt;li&gt;Product&lt;&#x2F;li&gt;
&lt;li&gt;HTML&lt;&#x2F;li&gt;
&lt;li&gt;Markdown&lt;&#x2F;li&gt;
&lt;li&gt;Картинка&lt;&#x2F;li&gt;
&lt;li&gt;Галерея&lt;&#x2F;li&gt;
&lt;li&gt;Кнопка&#x2F;Группа кнопок&lt;&#x2F;li&gt;
&lt;li&gt;Вставки
&lt;ul&gt;
&lt;li&gt;Youtube&lt;&#x2F;li&gt;
&lt;li&gt;X&lt;&#x2F;li&gt;
&lt;li&gt;Unsplash&lt;&#x2F;li&gt;
&lt;li&gt;Vimeo&lt;&#x2F;li&gt;
&lt;li&gt;CodePen&lt;&#x2F;li&gt;
&lt;li&gt;Spotify&lt;&#x2F;li&gt;
&lt;li&gt;SoundCloud&lt;&#x2F;li&gt;
&lt;li&gt;Telegram&lt;&#x2F;li&gt;
&lt;li&gt;Custom&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;&#x2F;details&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Сегмент можно перемещать по документу.
Сегмент можно расположить в зоне шаблона.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shablony&quot;&gt;Шаблоны&lt;&#x2F;h4&gt;
&lt;p&gt;К документу можно применить шаблон, который описывает какие сегменты могут
быть в документе и как они расположены.
К примеру, шаблон &lt;strong&gt;«Интервью»&lt;&#x2F;strong&gt; содержит в себе сегменты «Вопрос» и «Ответ»
помимо сегментов по умолчанию, таких как параграф.

    














    
    

    
    

    
    

    
    



    




&lt;figure class=&quot;breakout&quot;&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;soal.red&#x2F;ru&#x2F;stories&#x2F;cms-idea&#x2F;interview.jpg&quot; target=&quot;_blank&quot;&gt;
        &lt;picture&gt;
            
            
            
                
                    
                    
                
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.0dfd61ae86463306.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.550503312e8f8b16.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &lt;= 400px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.0c9dbdfabb3a7c15.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.cc6ab37095187fc0.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 400px) and (width &lt;= 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.c2fd86ac2c074ea4.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.665ec12e728eeb19.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;img
                    src=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;interview.c2fd86ac2c074ea4.avif&quot; alt=&quot;Выбор сегмента в шаблоне интервью&quot;
                    height=&quot;458&quot;
                    loading=&quot;lazy&quot;
                    width=&quot;857&quot;
                &gt;
            
        &lt;&#x2F;picture&gt;
    &lt;&#x2F;a&gt;
    
      &lt;figcaption&gt;Выбор сегмента в шаблоне интервью&lt;&#x2F;figcaption&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Шаблоны могут также описывать вёрстку и расположение сегментов относительно
друг друга для создания более динамичной структуры контента.

    














    
    

    
    

    
    

    
    



    




&lt;figure class=&quot;breakout&quot;&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;soal.red&#x2F;ru&#x2F;stories&#x2F;cms-idea&#x2F;segment-movement.jpg&quot; target=&quot;_blank&quot;&gt;
        &lt;picture&gt;
            
            
            
                
                    
                    
                
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.8afde406f53f9db8.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.4ec2e416b7179da5.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &lt;= 400px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.a07ab80d8cf80415.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.9555bbbf7817e7db.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 400px) and (width &lt;= 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.666714fe28988cf4.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.8f5b93333de499ca.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;img
                    src=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;segment-movement.666714fe28988cf4.avif&quot; alt=&quot;Шаблон в вёрсткой в две колонки&quot;
                    height=&quot;659&quot;
                    loading=&quot;lazy&quot;
                    width=&quot;857&quot;
                &gt;
            
        &lt;&#x2F;picture&gt;
    &lt;&#x2F;a&gt;
    
      &lt;figcaption&gt;Шаблон в вёрсткой в две колонки&lt;&#x2F;figcaption&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Нам нужна спецификация и API для создания и использования блоков.
Это позволит создавать специализированные продукты для разных сегментов
рынка и, в будущем, позволит пользователям создавать собственные шаблоны.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;predprosmotr&quot;&gt;Предпросмотр&lt;&#x2F;h4&gt;
&lt;p&gt;Редактор должен быть реализован таким образом, чтобы использовать стили
темы и сделать оформление в процессе редактирования максимально близким к
отображению публикации на сайте. Для этого надо реализовать загрузку стиля из
темы, установленной администратором сайта.
Отдельно нужен полноэкранный предпросмотр в двух режимах — мобильный и десктоп.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;vopros-markdown&quot;&gt;Вопрос Markdown&lt;&#x2F;h4&gt;
&lt;p&gt;В Ghost можно вставить Markdown отдельным блоком, который будет распознан
и трансформирован в HTML. Хотим ли мы сделать также или стоит создать
отдельный режим редактирования для Markdown, в котором редактор будет
трансформировать Markdown на лету (как, к примеру, в Obsidian)?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;vsio-dlia-perevoda&quot;&gt;Всё для перевода&lt;&#x2F;h3&gt;
&lt;p&gt;Перевод сайта должен быть всеобъемлющим, все компоненты системы должны быть
доступны для локализации.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;perevod-interfeisa&quot;&gt;Перевод интерфейса&lt;&#x2F;h4&gt;
&lt;p&gt;Система должна предоставлять возможность добавить перевод любых строк в
интерфейсе и набор функций для более глубокой локализации (форматы дат и
единиц измерения, варианты множественного числа)
Переводы должны хранится в одном или нескольких файлах (чтобы загружать их
отдельно по необходимости). В будущем нужно предоставить интерфейс для загрузки
перевода и базовой проверки на полноту.
Возможно, в будущем можно предоставить механизм автоматического перевода, но
это требует дополнительного исследования вопроса.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;perevod-kontenta&quot;&gt;Перевод контента&lt;&#x2F;h4&gt;
&lt;p&gt;Каждая сущность в системе (публикация, автор и прочие создатели, тэг и т.д.)
должна иметь версии на разных языках и собственный набор связей с другими
сущностями.
Публикации на разных языках могут иметь разный набор тэгов, авторов и
переводчиков, могут попадать в разные категории, если издание имеет разный
набор категорий на разных языках.
В связи с этим, каждая языковая версия должна быть отдельной записью в базе
данных, связанной с сестринскими версиями отдельным уникальным
идентификатором.
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;структура тэгов на разных языках&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;h4 id=&quot;perevod-publikatsii&quot;&gt;Перевод публикаций&lt;&#x2F;h4&gt;
&lt;p&gt;«Осень» должна предоставлять удобный интерфейс для перевода контента.
Каждый перевод это отдельный документ со своими авторами,
переводчиками, иллюстраторами и т.д., датами публикации и т.д.
Как указано выше, каждая языковая версия публикации должна быть отдельной
записью в БД и иметь общий уникальный идентификатор.&lt;&#x2F;p&gt;
&lt;p&gt;Переводы должны быть интегрированы в процесс работы и интерфейс.&lt;&#x2F;p&gt;
&lt;p&gt;Списки сущностей должны иметь фильтр по языку.
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;список публикаций отфильтрованных по языку&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Каждая публикация должна ясно показывать, на какие языки она переведена.
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;языки на которые переведена публикация&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Создание перевода должно быть легко доступно из интерфейса публикации.
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;интерфейс создания перевода&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Редактор должен поддерживать продвинутые методы работы с переводом.
Две версии рядом друг с другом
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;две версии перевода рядом друг с другом&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Предложение автоматического перевода с возможностью редактирования
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;автоматическая и окончательная версии перевода рядом друг с другом&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Интерфейс с оригиналом, переводом и предложенным автоматическим переводом
&lt;figure&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;em&gt;оригинал, окончательный и автоматический перевод рядом друг с другом&lt;&#x2F;em&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;p&gt;
&lt;h3 id=&quot;instrumenty-redaktora&quot;&gt;Инструменты редактора&lt;&#x2F;h3&gt;
&lt;p&gt;Для эффективной работы онлайн-издания CMS должна поддерживать гибкий рабочий
процесс, где каждая публикация может проходить через несколько этапов перед
публикацией.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;protsess-proizvodstva-i-publikatsii&quot;&gt;Процесс производства и публикации&lt;&#x2F;h4&gt;
&lt;p&gt;Публикация начинается с черновика, который доступен только в административном
интерфейсе.&lt;&#x2F;p&gt;
&lt;p&gt;Далее черновик может быть переведён в статус &quot;готово&quot; и ожидать публикации.
Здесь нам нужен интерфейс для обсуждения и редактуры материала, который
включает в себя:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Комментирование, привязанное к отдельным разделам материала&lt;&#x2F;li&gt;
&lt;li&gt;Обсуждения (треды) по каждому комментарию&lt;&#x2F;li&gt;
&lt;li&gt;Предложения изменений, которые могут быть приняты или модифицированы автором&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;После того, как работа закончена, материалу назначается время публикации,
включая публикацию немедленно, к определённому времени, или в специальный
режим &quot;ожидания&quot;, когда материал публикуется по триггеру, ручному или
автоматическому.&lt;&#x2F;p&gt;
&lt;p&gt;Статусы публикации:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Черновик&lt;&#x2F;strong&gt;. Назначается при создании нового материала&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Готов&lt;&#x2F;strong&gt;. Назначается, если материал требует этапа редактуры и не
публикуется немедленно.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Ожидает доработки&lt;&#x2F;strong&gt;. Назначается, если редактор вернул материал на
доработку автору.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Ожидает публикации&lt;&#x2F;strong&gt;. Назначается, если материалу назначено время
публикации в будущем.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;До звонка&#x2F;по триггеру&lt;&#x2F;strong&gt;. Назначается, если материал должен быть
опубликован по триггеру.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Опубликован&lt;&#x2F;strong&gt;. Назначается, когда материал опубликован в открытый доступ.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Отозван&lt;&#x2F;strong&gt;. Назначается, когда ранее опубликованный материал отозван.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Описанный выше порядок работы это &quot;программа-максимум&quot;. Администратор сайта
должен иметь возможность выбрать несколько вариантов порядка работы — &quot;полный&quot;,
&quot;упрощённый&quot; или какую-либо ещё комбинацию статусов.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;dostup-dlia-vneshnikh-avtorov&quot;&gt;Доступ для внешних авторов&lt;&#x2F;h4&gt;
&lt;p&gt;CMS должна иметь возможность предоставить доступ к редактированию
одной публикации для внешнего автора и последующего обсуждения при редактуре.
Для этого может потребоваться специальная роль и возможность авторизации
через внешние источники, которые поддерживают OAuth и аналогичные механизмы.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;sovmestnaia-rabota&quot;&gt;Совместная работа&lt;&#x2F;h4&gt;
&lt;p&gt;Система должна поддерживать одновременное редактирование несколькими
пользователями, сохраняя и совмещая изменения в виде CRDT.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;raspisanie-publikatsii&quot;&gt;Расписание публикаций&lt;&#x2F;h4&gt;
&lt;p&gt;«Осень» должна предоставлять интерфейс для контент-плана, в виде календаря с
запланированными публикациями и временными слотами.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;versionirovanie&quot;&gt;Версионирование&lt;&#x2F;h4&gt;
&lt;p&gt;Каждое изменение статуса публикации создаёт новую версию.
Система должна предоставлять механизм для просмотра истории версий и
изменений между версиями.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dostavka-kontenta&quot;&gt;Доставка контента&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;html-stranitsy&quot;&gt;HTML страницы&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;json-api&quot;&gt;JSON API&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;rss-i-atom&quot;&gt;RSS и Atom&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;integratsii&quot;&gt;Интеграции&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;seo&quot;&gt;SEO&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;mikroformaty&quot;&gt;Микроформаты&lt;&#x2F;h4&gt;
&lt;h3 id=&quot;analitika&quot;&gt;Аналитика&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;platezhi-i-podpiski&quot;&gt;Платежи и подписки&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;roli-prava-i-dostupy&quot;&gt;Роли, права и доступы&lt;&#x2F;h3&gt;
&lt;h2 id=&quot;struktura-dannykh&quot;&gt;Структура данных&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;publikatsii&quot;&gt;Публикации&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;resursy&quot;&gt;Ресурсы&lt;&#x2F;h3&gt;
&lt;h2 id=&quot;rendering-html-i-api&quot;&gt;Рендеринг HTML и API&lt;&#x2F;h2&gt;
&lt;p&gt;CMS должна предоставлять серверный рендеринг HTML и REST API — публичный для
отображения контента используя фронтенд пользователя и закрытый за авторизацией
для административной панели.&lt;&#x2F;p&gt;
&lt;p&gt;Возможно, нужно предусмотреть возможность отделить модуль рендеринга HTML и
запустить его вместе с веб-сервером на Edge-воркерах и подобных системах,
получая при этом контент из базы данных через API. Насколько это востребованный
и полезный функционал пока неясно.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;federatsiia&quot;&gt;Федерация&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;activitypub&quot;&gt;ActivityPub&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;atproto&quot;&gt;ATProto&lt;&#x2F;h3&gt;
</content>
        
    </entry>
    <entry xml:lang="ru">
        <title>Редакторы текста в Вебе</title>
        <published>2026-02-01T00:00:00+00:00</published>
        <updated>2026-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Alex Soal
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://soal.red/ru/stories/web-text-editors/"/>
        <id>https://soal.red/ru/stories/web-text-editors/</id>
        
        <content type="html" xml:base="https://soal.red/ru/stories/web-text-editors/">&lt;p&gt;Ниже краткий путеводитель по редакторам текста для Веба. Мы пройдёмся по
базовым технологиям, подходам и свободным библиотекам.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zadachi-kakie-my-redaktory-my-rassmatrivaem&quot;&gt;Задачи: какие мы редакторы мы рассматриваем?&lt;&#x2F;h2&gt;
&lt;p&gt;Мы будем исходить из задачи создания редактора текста общего назначения — для
редактирования публикаций в CMS, чат-клиенте, написания комментариев в
социальной сети и так далее. Нам нужно разнообразное форматирование:
общепринятые выделения &lt;em&gt;курсивом&lt;&#x2F;em&gt; и &lt;strong&gt;полужирным&lt;&#x2F;strong&gt;, заголовки и так далее. Также
нам нужны добавление изображений, видео, кода и прочих произвольных виджетов.&lt;&#x2F;p&gt;

    










    
    

    
    

    
    

    
    



    
    

    
    

    
    

    
    




    
    

    
    

    
    

    
    



    




&lt;figure class=&quot;breakout&quot;&gt;
  &lt;div class=&quot;double-frame&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;soal.red&#x2F;ru&#x2F;stories&#x2F;web-text-editors&#x2F;ghost-editor-light.png&quot; target=&quot;_blank&quot;&gt;
        &lt;picture&gt;
            
                
                    
                    
                
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.2cfd010b09463466.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.1e2655a769292ae0.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: dark) and (width &lt;= 400px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.40c1f09c28b20953.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.cc51e1dc37895ad6.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: dark) and (width &gt; 400px) and (width &lt;= 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.5dec03b330af4839.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-dark.814a6c5a949ccf00.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: dark) and (width &gt; 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
            
            
                
                    
                    
                
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.5c053ec120f916ee.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.9219b9674d9a6531.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &lt;= 400px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.f04f518042598374.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.765f250eb946aa7f.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 400px) and (width &lt;= 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.e7437cc1adcc4dec.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.43a5d1154e1e012f.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
            
            
                
                    
                    
                
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.5c053ec120f916ee.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.9219b9674d9a6531.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &lt;= 400px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.f04f518042598374.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.765f250eb946aa7f.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 400px) and (width &lt;= 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;source
                    srcset=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.e7437cc1adcc4dec.avif, https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.43a5d1154e1e012f.avif 2x&quot;
                    media=&quot;(prefers-color-scheme: light) and (width &gt; 800px)&quot;
                    type=&quot;image&#x2F;avif&quot;
                &gt;
                &lt;img
                    src=&quot;https:&amp;#x2F;&amp;#x2F;soal.red&amp;#x2F;processed_images&amp;#x2F;ghost-editor-light.e7437cc1adcc4dec.avif&quot; alt=&quot;Редактор текста в CMS Ghost&quot;
                    height=&quot;633&quot;
                    loading=&quot;lazy&quot;
                    width=&quot;857&quot;
                &gt;
            
        &lt;&#x2F;picture&gt;
    &lt;&#x2F;a&gt;
    
      &lt;figcaption&gt;Редактор текста в CMS Ghost. Типичный представитель софта, о котором мы будем говорить далее&lt;&#x2F;figcaption&gt;
    
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;obshchie-printsipy&quot;&gt;Общие принципы&lt;&#x2F;h2&gt;
&lt;p&gt;Рассмотренные здесь редакторы воплощают несколько общих базовых подходов,
на которых сошлись как на оптимальных:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conteditable-kak-baza&quot;&gt;Conteditable как база&lt;&#x2F;h3&gt;
&lt;h3 id=&quot;immutabel-nye-struktury-dannykh&quot;&gt;Иммутабельные структуры данных&lt;&#x2F;h3&gt;
&lt;p&gt;ProseMirror хранит контент и метаданные редактора в &lt;strong&gt;состоянии (state)&lt;&#x2F;strong&gt;, иммутабельной
структуре состоящей из объединённых в дерево &lt;strong&gt;нод&lt;&#x2F;strong&gt;&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
Такой подход стал общим местом и похожие решения используются в других
редакторах.
Автор ProseMirror описывает главное преимущество иммутабельности: при каждом
изменении контента создаётся новый объект состояния всего редактора, что
позволяет избежать «промежуточных» некорректных состояний.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tekstovye-koordinaty&quot;&gt;Текстовые координаты&lt;&#x2F;h3&gt;
&lt;h2 id=&quot;lexical&quot;&gt;Lexical&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;quill&quot;&gt;Quill&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;prosemirror&quot;&gt;ProseMirror&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;redaktory-na-osnove-prosemirror&quot;&gt;Редакторы на основе ProseMirror&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;tiptap&quot;&gt;TipTap&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;proseblock&quot;&gt;ProseBlock&lt;&#x2F;h4&gt;
&lt;h2 id=&quot;ckeditor&quot;&gt;CKEditor&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;overtype&quot;&gt;Overtype&lt;&#x2F;h2&gt;
&lt;p&gt;Достоин отдельного упоминания &lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;overtype.dev&#x2F;&quot;&gt;Overtype&lt;&#x2F;a&gt;
— простой редактор для Markdown, который
использует интересный трюк чтобы избежать всех сложностей описанных выше.
Он совсем из другой лиги, но идея отличная!&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;prosemirror.net&#x2F;docs&#x2F;guide&#x2F;#doc&quot;&gt;ProseMirror Guide&lt;&#x2F;a&gt; &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="ru">
        <title></title>
        <published>2025-11-04T00:00:00+00:00</published>
        <updated>2025-11-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Alex Soal
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://soal.red/ru/stories/smoll-modern-css/"/>
        <id>https://soal.red/ru/stories/smoll-modern-css/</id>
        
        <content type="html" xml:base="https://soal.red/ru/stories/smoll-modern-css/">&lt;p&gt;Заскочил чтобы написать что &lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;smolcss.dev&#x2F;&quot;&gt;SmolCSS&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;moderncss.dev&#x2F;&quot;&gt;Modern CSS&lt;&#x2F;a&gt; прекрасны. CSS прошёл большой путь за последние
несколько лет.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="ru">
        <title></title>
        <published>2025-10-29T00:00:00+00:00</published>
        <updated>2025-10-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Alex Soal
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://soal.red/ru/stories/perf-conf/"/>
        <id>https://soal.red/ru/stories/perf-conf/</id>
        
        <content type="html" xml:base="https://soal.red/ru/stories/perf-conf/">&lt;p&gt;Performance.now() это одна из конференций, на которые всё ещё стоит сходить
(или, по крайней мере, посмотреть выступления).
Фундаментальные темы, содержательные выступления.
Множество конференций превратились в маркетинговые мероприятия (вспоминается
VueConf. И почти всё, что касается Реакта)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="ru">
        <title>Зачем нужен VueMapbox</title>
        <published>2019-02-01T00:00:00+00:00</published>
        <updated>2019-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Alex Soal
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://soal.red/ru/stories/why-vue-mapbox/"/>
        <id>https://soal.red/ru/stories/why-vue-mapbox/</id>
        
        <content type="html" xml:base="https://soal.red/ru/stories/why-vue-mapbox/">&lt;h2 id=&quot;khoroshie-novosti&quot;&gt;Хорошие новости&lt;&#x2F;h2&gt;
&lt;p&gt;Библиотека &lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mapbox&#x2F;mapbox-gl-js&quot;&gt;Mapbox Gl JS&lt;&#x2F;a&gt; прекрасна.
Если вам нужно создать веб-приложение для работы с географическими картами,
реализовать в нём сложный функционал, то трудно найти инструмент лучше.
Она красивая.
Она быстрая.
Она открытая.
Она уникальна в своём роде: по-моему, это единственная живая и развиваемая
открытая реализация векторных карт в браузере. Был ещё MapZen, но его
разработка прекращена, так что выбор на самом деле не так уж велик.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vuejs.org&quot;&gt;Vue&lt;&#x2F;a&gt; тоже хорош. Это фреймворк для построения интерфейса в
котором найден хороший баланс между простой и функциональностью, &quot;магией&quot; и
возможностями для тонкой настройки.
Его система реактивности хорошо помогает в разработке сложных интерфейсов и
почти никогда не путается у вас под ногами. Почти… но это требует отдельного
поста 😉.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;novosti-pokhuzhe&quot;&gt;Новости похуже&lt;&#x2F;h2&gt;
&lt;p&gt;Сами по себе обе библиотеки прекрасны, а вот уживаются они не очень. Друг другу
они, конечно, не мешают, но если логика вашего интерфейса построена в парадигме
Vue и вы хотите и дальше пользоваться его преимуществами, то возникает вопрос:
как в эту парадигму вписывается Mapbox Gl JS?
Ответ не очень утешительный: примерно никак.&lt;&#x2F;p&gt;
&lt;p&gt;Тут необходимы пара небольших отступлений для прояснения вопроса.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deklarativnyi-stil-vue&quot;&gt;Декларативный стиль Vue&lt;&#x2F;h2&gt;
&lt;p&gt;Vue исповедует декларативный стиль программирования. Вы создаёте компонент,
передаёте в него данные в &lt;code&gt;props&lt;&#x2F;code&gt; и добавляете его в дерево компонентов.
Изменились данные — параметры прокидываются в компонент — компонент обновляется.
Для реализации в логике Vue, карта должна быть компонентом. Хотелось бы видеть
что-то вроде этого:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;vue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MyMap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapStyle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :center&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapCenterCoordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        …some&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; other&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; props…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;    &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Обычно на карте размещаются какие-нибудь объекты, например, маркеры, чтобы
отметить какие-либо места. Опять же, хотелось бы видеть их в виде компонентов,
которые естественно разместить внутри карты, так же, как мы бы поступили с
какими-то HTML-элементами. Если у нас есть список мест, то мы бы создали список
с данными маркеров и добавили их с помощью встроенной директивы &lt;code&gt;v-for&lt;&#x2F;code&gt;. Кроме
того, содержимое маркера хотелось бы менять в зависимости от условий, а также
добавлять и убирать их с помощью &lt;code&gt;v-if&lt;&#x2F;code&gt;. Что-то типа:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;vue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MyMap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapStyle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :center&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapCenterCoordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        …some&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; other&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; props…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;    &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MapMarker&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            v-for&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;marker in markers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            v-if&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;markerCondition&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            :coordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;marker.coordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;        &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;span&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-html&quot;&gt;I am marker!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;span&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MapMarker&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MyMap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Если данные меняются, то компоненты должны реагировать на это соответственно.
Vue предоставляет элегантное решение для двойного связывания (two-way data
binding) с помощью
&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vuejs.org&#x2F;v2&#x2F;guide&#x2F;components-custom-events.html#sync-Modifier&quot;&gt;synced props&lt;&#x2F;a&gt;.
Если какой-то метод меняет состояние карты (например, мы добавили красивый
перелёт карты к какому-то месту), было бы здорово использовать двойное
связывание для обновления данных.&lt;&#x2F;p&gt;
&lt;p&gt;Также, карта и её элементы могут генерировать события, и естественно слушать их
на компонетах самой карты и её элементов.&lt;&#x2F;p&gt;
&lt;p&gt;Как-то так (обратите внимание на &lt;code&gt;.sync&lt;&#x2F;code&gt; у параметра &lt;code&gt;center&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;vue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MyMap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapStyle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        :center.sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapCenterCoordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        @movend&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;moveHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;        …some&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; other&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt; props…&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;    &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MapMarker&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            v-for&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;marker in markers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            v-if&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;markerCondition&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            @click&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;markerClickHandler(marker)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-other z-attribute-name&quot;&gt;            :coordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;marker.coordinates&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;        &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;span&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-html&quot;&gt;I am marker!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;span&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MapMarker&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;MyMap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Всё как во Vue.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;imperativnyi-stil-mapbox-gl-js&quot;&gt;Императивный стиль Mapbox GL JS&lt;&#x2F;h2&gt;
&lt;p&gt;Проблема в том, что Mapbox GL JS это совершенно другой зверь и спроектиован для
работы в другой парадигме.
Для того, чтобы создать карту, нам надо вызвать её конструктор и передать ему
все необходимые параметры, включая &lt;code&gt;id&lt;&#x2F;code&gt; HTML-элемента, в котором будет
рисоваться карта. Конструктор вернёт нам объект &lt;code&gt;Map&lt;&#x2F;code&gt; с которым мы и должны
дальше работать.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Пример из документации Mapbox GL JS:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control z-import&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapbox-gl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    container&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapbox:&#x2F;&#x2F;styles&#x2F;mapbox&#x2F;streets-v9&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Если мы хотим, к примеру, поменять центр карты, нам надо вызвать
соответствующий метод:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control z-import&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapbox-gl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …map config… &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;setCenter&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;И так далее. Любые манипуляции с картой делаются через методы этого объекта.
Тоже самое с получением данных. Чтобы узнать координаты центра карты, мы должны
вызвать соответсвующий метод:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control z-import&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mapbox-gl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …map config… &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; center&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;getCenter&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Если мы хотим слушать события карты, то нужно добавить обработчики событий к
самому объекту &lt;code&gt;Map&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …map config… &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; moveHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …some actions… &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;moveend&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; moveHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;И если элементы карты, например, слои, генерируют какие-то события, то нам
нужно опять же слушать их на самом объекте карты, передав идентификатор слоя в
качестве «фильтра»:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; map&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mapboxgl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; layerId&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;myLayer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; clickHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …some actions… &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;click&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; myLayer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; clickHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Не очень-то похоже на наш гипотетический пример выше в декларативном стиле…
Ах да, слои. О слоях стоит сказать несколько слов отдельно.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sloi-v-mapbox-gl-js&quot;&gt;Слои в Mapbox GL JS&lt;&#x2F;h3&gt;
&lt;p&gt;Сами слои не представлены в виде объектов в API Mapbox GL. Чтобы добавить слой
на карту нужно вызвать метод карты &lt;code&gt;.addLayer()&lt;&#x2F;code&gt;, указав источник данных.
&lt;em&gt;Пример из документации Mapbox GL JS&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; function&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;addLayer&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;main&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;source&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;geojson&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; …GeoJSON data &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;layout&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;paint&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fill-color&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;#088&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fill-opacity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Все дальнейшие манипуляции производятся также с помощью методов карты с
передачей &lt;code&gt;id&lt;&#x2F;code&gt; слоя в качестве аргумента. Всё это значит, что работать со
слоями в объектно-ориентированном и декларативном стиле очень неудобно. В
частности, для добавления или удаления слоёв было бы удобно использовать хуки
жизненного цикла Vue, однако напрямую это невозможно.
По сути, для этого нам нужно реализовать отдельную прослойку, которая будет
предоставлять декларативный API и заботится о трансляции всех необходимых
изменений на карту.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;asinkhronnye-metody-i-sobytiia&quot;&gt;Асинхронные методы и события&lt;&#x2F;h2&gt;
&lt;p&gt;В одном из проектов, с которыми я работал, требовалось последовательно
показывать ряд анимаций на карте, останавливая&#x2F;изменяя их в зависимости от
действий пользователя, и я столкнулся с тем, что определить момент, когда
асинхронные методы карты, такие как &lt;code&gt;.flyTo()&lt;&#x2F;code&gt;, закончили работу, не так-то
просто. В JavaScript для этих целей существет &lt;code&gt;Promise&lt;&#x2F;code&gt;. Но, к сожалению,
асинхронные методы не возвращают Promise и, судя по
&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mapbox&#x2F;mapbox-gl-js&#x2F;issues&#x2F;3964&quot;&gt;этому фич-реквесту&lt;&#x2F;a&gt;,
серьёзных подвижек в этом направлении в ближайшее время не предвидится.
Mapbox GL JS генерирует события, когда какое-либо действие заканчивается,
например, события &lt;code&gt;moveend&lt;&#x2F;code&gt;, &lt;code&gt;rotateend&lt;&#x2F;code&gt; etc. Можно подписаться на эти события,
но если на карте происходит несколько действий одновременно или порядок их
может меняться, то как нам определить, какое имеено действие вызвало то или
иное событие?
Механизм для этого есть, но его трудно назвать удобным в использовании.&lt;&#x2F;p&gt;
&lt;p&gt;В асинхронные методы карты можно передать объект &lt;code&gt;eventData&lt;&#x2F;code&gt; в качестве
аргумента. Когда какое-либо дествие с картой заканчивается, Mapbox GL JS
генерирует событие и возвращает в качестве нагрузки этот объект. Таким образом,
мы можем сгененрировать уникальный ID для каждого действия и по нему определить,
какой именно метод вызвал определённое событие:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; actionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uniqueActionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;moveend&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;actionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uniqueActionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Do something!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;flyTo&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; center&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; actionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uniqueActionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Второй вариант — передать в &lt;code&gt;eventData&lt;&#x2F;code&gt; коллбэк, и вызвать его когда поймается событие.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; console&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Done!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;on&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;moveend&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; event&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property&quot;&gt;callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;flyTo&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; center&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Делать так каждый раз довольно утомительно и не очень красиво, так что я
написал небольшую библиотеку
&lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;soal&#x2F;map-promisified&quot;&gt;map-promisified&lt;&#x2F;a&gt;, которая оборачивает
методы Mapbox GL JS в асинхронные функции, кторые возвращают &lt;code&gt;Promise&lt;&#x2F;code&gt;. Когда
действие заканчивается, &lt;code&gt;Promise&lt;&#x2F;code&gt; разрешается, возвращая данные об изменившихся
параметрах карты.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;schastlivyi-konets-vuemapbox&quot;&gt;Счастливый конец: VueMapbox&lt;&#x2F;h2&gt;
&lt;p&gt;В результате упорной борьбы с несовпадением парадигм программирования двух
замечательных библиотек, родилась новая: &lt;a rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;soal.github.io&#x2F;vue-mapbox&quot;&gt;Vue-Mapbox&lt;&#x2F;a&gt;.
Она решает описанные выше проблемы, предоставляя удобный декларативный API для
работы с картой.
Распространияется под лицензией MIT и весит около 7Кб в сжатом виде.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a
  href=&quot;https:&amp;#x2F;&amp;#x2F;soal.github.io&amp;#x2F;vue-mapbox&quot;
  target=&quot;_blank&quot;
  rel=&quot;nofollow&quot;
  rel=&quot;noopener&quot;
  rel=&quot;noreferrer&quot;
  class=&quot;linkbox frame&quot;
&gt;
  
    
  
  
    
      &lt;svg
   width=&quot;163&quot;
   height=&quot;200&quot;
   viewBox=&quot;0 0 163 200&quot;
   fill=&quot;none&quot;
   version=&quot;1.1&quot;
   id=&quot;svg5&quot;
   xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;
   xmlns:svg=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
  &lt;defs
     id=&quot;defs5&quot; &#x2F;&gt;
  &lt;path
     fill-rule=&quot;evenodd&quot;
     clip-rule=&quot;evenodd&quot;
     d=&quot;M163 73.2769C162.805 146.266 81.7143 200 81.7143 200C81.7143 200 -0.195142 146.892 0.000292271 73.6636C0.108582 33.0876 36.7436 0.227932 81.6961 0.00125302C126.649 -0.225426 163.108 32.7009 163 73.2769ZM81.7143 166.861C81.7143 166.861 139.154 129.126 139.154 77.6847C139.154 49.0873 113.396 25.8202 81.7143 25.9048C50.0327 25.9894 24.2744 49.0873 24.2744 77.6848C24.2744 129.296 81.7143 166.861 81.7143 166.861Z&quot;
     fill=&quot;#41B883&quot;
     id=&quot;path1&quot; &#x2F;&gt;
  &lt;path
     fill-rule=&quot;evenodd&quot;
     clip-rule=&quot;evenodd&quot;
     d=&quot;M81.7143 200C81.7143 200 162.805 146.266 163 73.2769C163.108 32.7009 126.649 -0.225426 81.6961 0.00125302C36.7436 0.227932 0.108582 33.0876 0.000292271 73.6636C-0.195142 146.892 81.7143 200 81.7143 200ZM81.7143 166.861C81.7143 166.861 139.154 129.126 139.154 77.6847C139.154 49.0873 113.396 25.8202 81.7143 25.9048C50.0327 25.9894 24.2744 49.0873 24.2744 77.6848C24.2744 129.296 81.7143 166.861 81.7143 166.861Z&quot;
     fill=&quot;#41B883&quot;
     id=&quot;path2&quot; &#x2F;&gt;
  &lt;path
     fill-rule=&quot;evenodd&quot;
     clip-rule=&quot;evenodd&quot;
     d=&quot;M140 77.3929C139.861 129.58 82.1526 168 82.1526 168C82.1526 168 23.8612 130.028 24.0003 77.6694C24.0773 48.6576 50.1489 25.1629 82.1396 25.0008C114.13 24.8388 140.077 48.3811 140 77.3929ZM82.1526 144.306C82.1526 144.306 123.03 117.325 123.03 80.5445C123.03 60.0974 104.699 43.4614 82.1526 43.5219C59.6061 43.5823 41.2751 60.0974 41.2751 80.5445C41.2751 117.446 82.1526 144.306 82.1526 144.306Z&quot;
     fill=&quot;#35495E&quot;
     id=&quot;path3&quot; &#x2F;&gt;
  &lt;path
     fill-rule=&quot;evenodd&quot;
     clip-rule=&quot;evenodd&quot;
     d=&quot;M82.1526 168C82.1526 168 139.861 129.58 140 77.3929C140.077 48.3811 114.13 24.8388 82.1396 25.0008C50.1489 25.1629 24.0773 48.6576 24.0003 77.6694C23.8612 130.028 82.1526 168 82.1526 168ZM82.1526 144.306C82.1526 144.306 123.03 117.325 123.03 80.5445C123.03 60.0974 104.699 43.4614 82.1526 43.5219C59.6061 43.5823 41.2751 60.0974 41.2751 80.5445C41.2751 117.446 82.1526 144.306 82.1526 144.306Z&quot;
     fill=&quot;#35495E&quot;
     id=&quot;path4&quot; &#x2F;&gt;
  &lt;path
     d=&quot;M81.2756 114.551L90.5236 95.5236L109.551 86.2756L90.5236 77.0276L81.2756 58L72.0276 77.0276L53 86.2756L72.0276 95.5236L81.2756 114.551Z&quot;
     fill=&quot;#41B883&quot;
     id=&quot;path5&quot; &#x2F;&gt;
&lt;&#x2F;svg&gt;

    
  
  &lt;b class=&quot;link&quot;&gt;Vue Mabpox&lt;&#x2F;b&gt;
  &lt;i&gt;&lt;small&gt;Документация&lt;&#x2F;small&gt;&lt;&#x2F;i&gt;
&lt;&#x2F;a&gt;

&lt;a
  href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;soal&amp;#x2F;vue-mapbox&quot;
  target=&quot;_blank&quot;
  rel=&quot;nofollow&quot;
  rel=&quot;noopener&quot;
  rel=&quot;noreferrer&quot;
  class=&quot;linkbox frame&quot;
&gt;
  
    
  
  
    
      &lt;svg width=&quot;98&quot; height=&quot;96&quot; viewBox=&quot;0 0 98 96&quot; fill=&quot;none&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;g clip-path=&quot;url(#clip0_730_27126)&quot;&gt;
&lt;path d=&quot;M41.4395 69.3848C28.8066 67.8535 19.9062 58.7617 19.9062 46.9902C19.9062 42.2051 21.6289 37.0371 24.5 33.5918C23.2559 30.4336 23.4473 23.7344 24.8828 20.959C28.7109 20.4805 33.8789 22.4902 36.9414 25.2656C40.5781 24.1172 44.4062 23.543 49.0957 23.543C53.7852 23.543 57.6133 24.1172 61.0586 25.1699C64.0254 22.4902 69.2891 20.4805 73.1172 20.959C74.457 23.543 74.6484 30.2422 73.4043 33.4961C76.4668 37.1328 78.0937 42.0137 78.0937 46.9902C78.0937 58.7617 69.1934 67.6621 56.3691 69.2891C59.623 71.3945 61.8242 75.9883 61.8242 81.252L61.8242 91.2051C61.8242 94.0762 64.2168 95.7031 67.0879 94.5547C84.4102 87.9512 98 70.6289 98 49.1914C98 22.1074 75.9883 6.69539e-07 48.9043 4.309e-07C21.8203 1.92261e-07 -1.9479e-07 22.1074 -4.3343e-07 49.1914C-6.20631e-07 70.4375 13.4941 88.0469 31.6777 94.6504C34.2617 95.6074 36.75 93.8848 36.75 91.3008L36.75 83.6445C35.4102 84.2188 33.6875 84.6016 32.1562 84.6016C25.8398 84.6016 22.1074 81.1563 19.4277 74.7441C18.375 72.1602 17.2266 70.6289 15.0254 70.3418C13.877 70.2461 13.4941 69.7676 13.4941 69.1934C13.4941 68.0449 15.4082 67.1836 17.3223 67.1836C20.0977 67.1836 22.4902 68.9063 24.9785 72.4473C26.8926 75.2227 28.9023 76.4668 31.2949 76.4668C33.6875 76.4668 35.2187 75.6055 37.4199 73.4043C39.0469 71.7773 40.291 70.3418 41.4395 69.3848Z&quot; fill=&quot;currentColor&quot;&#x2F;&gt;
&lt;&#x2F;g&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;clip0_730_27126&quot;&gt;
&lt;rect width=&quot;98&quot; height=&quot;96&quot; fill=&quot;white&quot;&#x2F;&gt;
&lt;&#x2F;clipPath&gt;
&lt;&#x2F;defs&gt;
&lt;&#x2F;svg&gt;

    
  
  &lt;b class=&quot;link&quot;&gt;Исходный код&lt;&#x2F;b&gt;
  &lt;i&gt;&lt;small&gt;Vue and JavaScript&lt;&#x2F;small&gt;&lt;&#x2F;i&gt;
&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
