image

Фабрика виджетов — способ организации клиентского кода, который отлично вписывается в архитектуру multi-page application (MPA).

В статье будет рассмотрено архитектурное решение, которое позволит оптимизировать загрузку скриптов, разделить код на виджеты и упростит передачу данных на клиент со страницы (при серверном рендеринге).
Виджетами будут называться компоненты (или контейнеры компонентов), точкой инициализации которых будет DOM. И в которые можно будет передать данные из шаблона.
А сейчас обо всем последовательно.

Концепция


На странице размещаются информация о вызываемых виджетах (имя виджета, данные для него).

Логика реализации будет в основном и единственном подключаемом бандле, состоящем из:

  1. Скриптов которые выполняются на большинстве страниц нашего приложения
  2. Код для всех common-виджетов
    Здесь и далее под common-виджетами будут пониматься те виджеты, которые чаще всего необходимы в приложении, например: навигация, поиск.
  3. Хеш-список всех lazy-виджетов*, содержащий их имена и пути к реализации
    Здесь и далее под lazy-виджетами будут пониматься те виджеты, которые нужны в редких случаях. Забегая вперёд, такие виджеты будут загружаться динамически, например: галерея, маркетинговые окна.
  4. Непосредственно код фабрики виджетов

Чтобы лучше понять концепцию, разберем пример реализации.

Реализация


Сама реализация архитектуры довольно проста, с ее вариантом можно ознакомиться на гитхабе. В ее основу лег Webpack и его динамический импорт.

Далее пробежимся по основным логическим пунктам:

  1. На странице размещаются тег script, содержащий имя виджета, данные в виде JSON

    <script type="application/widget+json">
     {
         "widget": "TesLazyWidget",
          "data": {
              "1": 1
          }
       }
     </script>
    
  2. Подключается entry.js
  3. Выполняются корнер-скрипты
  4. Фабрика виджетов находит все теги script

    initWidgets($('script[type="application/widget+json"]'));
    
  5. Разбор JSON

    data = $.parseJSON(script.innerHTML);
    
  6. Если это common-виджет, то инициализирует и запоминает его

    widget = new Widget(data);
    
  7. Если это lazy-виджет то загружает его

    loaderWidget().then(createWidget);
    
  8. Стартует все запомненные common-виджеты

    for (let i = 0; i < this.widgets.length; i++) {
      this.widgets[i].start();
    }
    
  9. Инициализирует и стартует загруженные lazy-виджеты

    const widget = new Widget(data);
    widget.start();
    

Реализация схематично.

image

Основные преимущества данной архитектуры


Увеличение скорости загрузки страниц


Основной бандл содержит только критические скрипты, основная функциональность. Дополнительная логика будет загружена динамически. В таком бандле будет высокий процент покрытия кода (code coverage). Это уменьшает его размер страницы, и как следствие повышает производительность.

Например, сразу загрузится меню и информация о пользователе, а динамически загрузится рекламное предложение, галерея картинок и дополнительные визуальные эффекты.

Это хорошая альтернатива роутингу


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

Упрощенный способ передачи данных на клиент


Просто отрендерив JSON на странице, уже на этапе разбора данные попадают на клиент, без необходимости дополнительных AJAX-запросов.

    <script type="application/widget+json">
            {     "widget": "ActionBar"
                , "data": {
                   "isPublic": true,
                   "id": "{{ id }}",
                   "items" : [{
                      "title": "Edit",
                      "iconLeft": "edit"
                   }]
                }
            }
    </script>

Уменьшение дублирования кода


Переиспользовать виджет? Легко! Например, на странице нужно реализовать несколько списков новостей с фильтрами, подгрузкой и прочей интерактивной логикой. За реализацию этого функционала будет отвечать виджет NewsList. Аналогично html-тегам, достаточно просто вставить в шаблон вызов этого виджета столько, сколько нужно списков, и везде требуется.

        <script type="application/widget+json">
            {
                "widget": "NewsList",
                "data": {
                    "contentId": "business"
                }
            }
        </script>
        <script type="application/widget+json">
            {
                "widget": "NewsList",
                "data": {
                    "contentId": "policy"
                }
            }
        </script>

Код становится читабельнее и понятнее


Фабрика виджетов формализует правила написания кода, что в свою очередь создает четкую структуру инициализации и выполнения скриптов. В этом подходе цепочка вызова виджетов линейна. Логика каждого из них инкапсулирована, и, как результат, — меньше непредвиденных последствий от изменения кода, меньше сумбура в проекте, большая гибкость системы.

Отличный способ организации кода, для “старых” проектов


Когда в приложении все уже написано, но без особой идеи. Постоянное ощущение бардака не покидает. И в проекте сплошь и рядом инлайн скрипты

    <script>
        require(['jquery', 'selectBoxIt'], function ($) {
            $('#itemId_selector').selectBoxIt();
            var $form = $('#savepost');

    ……..

            $('#skip').click(function () {
                $form.submit();
            });
        });
    </script>

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

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

Вместо заключения


В итоге в архитектуру MPA фабрика виджетов отлично вписывается. Формализует код, оптимизирует загрузку и дает четкие и удобные правила по использованию.

Комментарии (2)


  1. geleont
    06.10.2017 21:56

    Мы использовали подобный подход на одном из проектов. На самом деле показывает очень хорошие результаты. Нам удалось снизить объем загружаемого кода более чем на 30%, а на некоторых страницах даже больше. И это без больших вложений в рефакторинг.


  1. novoxudonoser
    07.10.2017 02:19

    Хм, это всё классно. Расово одобряю, особенно за такой мелкий нюанс как чёрные рабочие на заводе на пикче.