Правила проектов на Wasaby

На Wasaby разрабатываются достаточно сложные и объемные проекты. Для повышения качества кода и уменьшения количества потенциальных проблем мы разработали свод правил, которым надо следовать при разработке интерфейсов на Wasaby.

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

Продуктовые требования к интерфейсу

  1. Все интерфейсы должны строиться из стандартных компонентов (стандарт, документация, API). Любые отклонения должны быть согласованы с отделом стандартов на этапе согласования макета и ТЗ по проекту;
  2. Элементы интерфейса не должны прыгать: однажды появившись, позиция и размер элемента должны быть зафиксированы на все время инициализации (некоторые элементы могут строиться лениво, подробнее ниже);
  3. Весь текст должен быть выровнен по базовой линии (документация);
  4. Все компоненты должны корректно обрабатывать исключения и выводить дружелюбные к пользователю ошибки (стандарт, документация);
  5. Должны быть реализованы пустые представления – в реестре нет данных, нет данных в конкретной папке и нет данных из-за поставленного фильтра (стандарт, документация);
  6. Поддержка интернационализации для продуктов, включенных в список (документация);
  7. Карточки должны быть созданы по правилам описания диалога редактирования. Открытие карточки из реестра должно происходить по данным из реестра, не дожидаясь вызова метода БЛ;
  8. Вывод даты должен соответствовать стандарту и осуществляться с помощью платформенного API;
  9. Вывод картинок должен поддерживать экраны с повышенной плотностью пикселей;
  10. Должны быть проработаны все аспекты использования :hover;
  11. Следует учитывать особенности работы на iPad.

Загрузка и инициализация приложения

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

Все страницы должны поддерживать серверную верстку и строиться на сервисе представления. Последовательное появление элементов на странице допустимо (пример – задачи).

Таким образом, решаются проблемы медленных запросов. Запросы делятся на 2 части:

  • Часть данных можно запросить максимально быстро для отображения основных данных в интерфейсе. При открытии диалога редактирования возможно построение карточки без запроса – по данным из реестра;
  • Инициализация запроса получения долго вычисляемых данных должна происходить вместе с первым запросом, но отрисовка будет проходить во второй этап.

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

В случае долгой загрузки ресурса, индикатор не должен появляться ранее 2 секунд. Используйте контрол Controls.LoadingIndicator.

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

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

Требования специфичные для страниц на online.sbis.ru

  1. Все страницы на online.sbis.ru должны быть построены в соответствии с инструкцией;
  2. Требования к страницам с аккордеоном:
    • Страницы должны быть описаны в navx файлах и работать в SPA режиме;
    • Должна быть реализована предзагрузка данных через роутинг (кроме случаев, когда данные необходимо загружать на клиенте);
    • Работа с вкладками должна быть реализована через роутинг.
  3. Адреса страницы должны соответствовать требованиям;
  4. На всех страницах должны быть реализованы online-специфичные элементы системы помощи:
  5. Интерфейс должен быть разработан с учетом изменения размеров рабочей области (стандарт, документация).

Требования к производительности

  1. Все новые страницы и панели должны укладываться в бюджет производительности;
  2. Не создавайте тысячи контролов. Несмотря на то, что в основе построения архитектуры клиентского приложения СБИС лежит Wasaby-контрол, не стоит злоупотреблять этим инструментом. Особенно когда имеете дело со списками. Очень часто люди делают ошибку, размещая контролы-контроллеры в каждой строке списка, в то время, когда можно было бы обойтись одним общим;
  3. Во всех новых проектах должны быть организованы JavaScript библиотеки:
    • Компоненты не должны прилетать на страницу отдельными запросами;
    • Нужно правильно организовывать библиотеки, чтобы на страницы не грузился неиспользуемый код. Минимальный Code Coverage загружаемых библиотек на страницу до выполнения действий должен быть не менее 60%.
  4. Во всех новых проектах должны быть организованы CSS библиотеки;
  5. Все компоненты, которые не отображаются при первоначальной загрузке страницы должны грузиться лениво;
  6. Компоненты не должны менять свой стейт в afterMount. Это приводит к лишним перестроениям компонентов при инициализации и тормозит оживление. Стейт компонента должен инициализировать в beforeMount и компонент должен строиться за один цикл синхронизации. Лишние перерисовки можно отследить через профилировщик в Wasaby DevTools;
  7. Необходимо организовывать оптимальную структуру данных приходящих с БЛ, чтобы не приходилось выполнять сложные расчёты на клиенте. У пользователя может быть слабый компьютер и гораздо эффективнее все необходимые вычисления осуществлять на сервере;
  8. С БЛ на клиент не должно прилетать больше данных чем нужно для отображения. Такие ситуации часто возникают, когда один метод БЛ используется для построения разных интерфейсов.

Требования к коду

  1. Код должен соответствовать стайлгайдам:
  2. Все новые проекты должны быть написаны на TypeScript;
  3. Не должно быть очень больших файлов (1000+ строк);
  4. Не должны быть очень больших методов (100+ строк);
  5. Необходимо избегать дублирования кода;
  6. Код должен быть хорошо задокументирован и прокомментирован, чтобы сторонний разработчик мог понять как работает тот или иной метод;
  7. У всех модулей должен быть написан автор и краткое описание;
  8. Подключение сторонних библиотек возможно только после согласования.

Нередко бывают ситуации, когда требуется добавить временный "костыль" для решения какой-то проблемы. В таком случае обязательно должна быть выписана задача, по выполнению которой код будет исправлен. В месте написание "костыля" должно быть добавлено TODO со ссылкой на задачу.

TypeScript

Код компонентов должен соответствовать стандартам разработки контролов на TypeScript.

LESS

  1. В новых проектах не должно быть CSS файлов – только LESS;
  2. Все классы должны быть написаны по БЭМ, каскады должны использовать по минимуму.
  3. Не должны дублироваться значения свойств (цвета, размеры шрифтов), необходимо использовать переменные. Переменные должны инициализироваться с помощью переменных из контролов.
    Можно использовать все переменные из _aliasses.less.
    • Неправильно:
      .myModule-MyComponent__container_error {
          border-color: red;
      }
    • Правильно:
      @import 'Controls-theme/themes/default/default';
          .myModule-MyComponent__container_error {
              border-color: @danger_color;
      }
  4. Все стили на ховер должны быть обернуты в .ws-is-hover (чтобы корректно отрабатывало на мобильных устройствах).
    • Неправильно:
      .myModule-MyComponent__container:hover {
          background-color: @background-color_MyComponent_hovered;
      }
    • Правильно:
      .ws-is-hover {
          .myModule-MyComponent__container:hover {
              background-color: @background-color_MyComponent_hovered;
         }
      }
  5. Все классы должны называться осмысленно на основе их назначения, а не конкретной текущей реализации.
    • Неправильно:
      .white {
          color: #fff;
      }
    • Правильно:
      .myModule-MyComponent__container_selected {
          color: @color_MyComponent_selected;
      }
  6. Нельзя генерировать верстку в TS и вставлять ее в DOM. Вся верстка должна строиться через шаблоны;
  7. При написании стилей для компонентов на online.sbis.ru запрещено использовать медиа запросы. Вместо этого нужно пользовать классы с шириной рабочей области.

WML

  1. Не надо вставлять пустой корневой тэг в шаблоне. Вставляемый компонент сам по себе является корневым элементом.
    • Неправильно:
      <div>
         <Controls.buttons:Button/>
      </div>
    • Правильно:
      <Controls.buttons:Button/>
  2. Если элемент интерфейса отображается в зависимости от какого-то условия, то он должен скрываться через ws:if, а не с помощью стилей.
    • Неправильно:
      <div>
         <ControlOne attr:class="{{ !_controlOneVisible ? 'ws-hidden' : '' }}"/>
         <ControlTwo attr:class="{{ _controlOneVisible ? 'ws-hidden' : '' }}"/>
      </div>
    • Правильно:
      <div>
         <ws:if data="{{ _controlOneVisible }}">
            <ControlOne />
         </ws:if>
         <ws:else>
            <ControlTwo />
         </ws:else>
      </div>
  3. Шаблоны должны быть читабельные – в них не должно быть сложных вычислений опций и условий. Лучше вычислить состояние в TS и его проверять в шаблоне
  4. Inline стили в шаблонах могут использоваться, только если значения свойств необходимо рассчитывать в TS. В остальных случаях все стили должны быть описаны в .less файле.
  5. Изменение DOM должно происходить исключительно через virtual dom. Запрещено вручную обращаться к DOM элементам и изменять их.

С недавнего времени все ссылки на статические ресурсы декорируются с помощью функции getResourceUrl, что позволяет запрашивать ресурсы через внешние CDN-сервисы, тем самым распределяя нагрузку на сервера.

Чтобы задекорировать ссылки через getResourceUrl в wml/tmpl-шаблонах, необходимо:

  1. Обернуть все ссылки в функцию getResourceUrl.
    Например, имеем следующую ссылку на ресурс в шаблоне:
    <audio class="WebRTC-Window__Beeps" loop="loop" src="/cdn/VideoRC/audio/1.0.1/o_call.mp3"></audio>
    В результате должна получиться ссылка:
    <audio class="WebRTC-Window__Beeps" loop="loop" src="{{getResourceUrl('/cdn/VideoRC/audio/1.0.1/o_call.mp3')}}"></audio>

    Необходимо оборачивать ссылку в одинарные ковычки, иначе шаблонизатор выдаст вам ошибку.
  2. В компоненте, использующем ваш шаблон, необходимо передать функцию getResourceUrl в опции, чтобы она была доступна при компиляции вашего шаблона.
    • Для ts-компонентов:
      Пример подключения в модуле:
      import getResourceUrl = require('Core/helpers/getResourceUrl');
      Пример передачи в опциях:
      protected getResourceUrl: Function = getResourceUrl;

      Рекомендуется описывать как protected, иначе возникнет ошибка при компиляции.
    • Для js-компонентов:
      Подключение в модуле выполняется согласно стандартному правилу подключения require-зависимостей.
      Пример передачи в опциях:
      this.getResourceUrl = getResourceUrl;

Принцип передачи getResourceUrl в компоненте ничем не отличается от передачи в опциях для wml/tmpl.

Разница присутствует только в использовании getResourceUrl в шаблоне.

Пример:

<link rel="preload" as="font" href="/cdn/EmojiFont/1.0.1/TFEmojiFont.woff2" type="font/woff2" crossorigin="crossorigin" />

После декорирования ссылка примет вид:

<link rel="preload" as="font" href="{{=it.getResourceUrl('/cdn/EmojiFont/1.0.1/TFEmojiFont.woff2')}}" type="font/woff2" crossorigin="crossorigin" />

В заключение рассмотрим пример правильного декорирования ссылки на статический ресурс из обычного Интерфейсного модуля.

Исходная ссылка в шаблоне имеет вид:

<script key="bundles" type="text/javascript" src="{{resourceRoot}}bundles.js"></script>

После декорирования она должна принять вид:

<script key="config" type="text/javascript" src="{{getResourceUrl(resourceRoot + 'WS.Core/ext/requirejs/config.js')}}"></script>

Правильное использование готовых контролов

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

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

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

Не допускается какое-либо изменение стандартного поведения контролов Wasaby. Не допускается обращаться к свойствам и методам private и protected областей видимости (protected доступна только при наследовании).

Если вы не можете решить свои задачу через публичное API контрола, необходимо задать вопрос в сервисе Вопрос-ответ и проконсультироваться с ответственным разработчиком. Он либо предложит способ решения задачи, либо выпишет задачу на доработку компонентов.

Переопределение стилей

Не допускается перекрытие стилей платформенных контролов с помощью !important или усиленных CSS

Неправильно:

.wnc-controls__spoilertoggler-btn._collapsed .controls-BaseButton__text {
    text-decoration: none !important;
    color: #8991A9;
}

Что делать если вам выписали задачу на реализацию интерфейса, который визуально не соответствует стандартным компонентам:

  1. Необходимо обсудить с ответственным проектировщиком возможность приведения макета к стандарту. Большая часть проблем с переопределением стилей решается именно через изменение макета. Бывают случаи, когда проектировщик даже не заметил, что отклонился от стандарта;
  2. Проконсультироваться с ответственным разработчиком через сервис Вопрос-ответ и обсудить возможность расширения API компонента (к примеру, добавление опции fontColor для изменения цвета шрифта);
  3. Расширить стандарт под требования вашего макета выписав поручение Батуриной Н.;
  4. Создать самостоятельную тему оформления. При этом обязательно необходимо согласовать добавление темы с Бегуновым А. Если вы разрабатываете интерфейс в рамках online.sbis.ru, создание своей темы не допускается;
  5. Согласовать с Бегуновым А. или Крайновым Д. спец. решение с использованием усиленного CSS-каскада или !important. В исходном коде по месту использования должен быть комментарий со ссылкой на согласование.

Если вы обсудили ваш вопрос с платформой и стандартами и решили, что будет дорабатываться стандартный компонент, но при этом у вас нет времени ждать доработки, разрешается перебить платформенные стили и рядом оставить TODO со ссылкой на задачу, по выполнению которой «костыль» можно будет убрать.

Правильные контролы в правильных местах

  1. Запрещается создание своих контролов дублирующих поведение платформенных компонентов. Список платформенных компонентов можно посмотреть в автодокументации на wi. Если вы не уверены существует ли платформенный компонент для решения ваших задач, можно задать вопрос в группе Wasaby;
  2. Стандартные контролы должны использоваться по назначению. К примеру, были случаи когда поле ввода использовалось и стилизовалось как кнопка. Такое использование недопустимо.

Правильное использование библиотеки представления

Организация взаимодействия между компонентами

Взаимодействие между компонентами должно быть реализовано через опции, события и методы. Другие способы взаимодействия (глобальная шина событий, всплывающие события, event:Listener) должны использовать только в случаях, когда другие варианты взаимодействия являются невозможными.

Bubbling события

Основная проблема всплывающих событий в том, что они фактически расширяют API компонентов, расположенных выше по иерархии. Таким образом получается, что компоненты начинают стрелять событиями, которых нет у них в API. Это может привести к тому, что произойдет пересечение имен событий и вышестоящие компоненты отработают некорректно.

Нужно стараться не использовать bubbling события. Компоненты должны стрелять только своими событиями.

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

Опции и методы

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

Жизненный цикл компонента

beforeUpdate

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

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

Неправильно:

_beforeUpdate(newOptions) => {
    this.data = newOptions.source.query();
}

Правильно:

_beforeUpdate(newOptions) => {
    if (newOptions.source !== this._options.source) {
        this.data = newOptions.source.query();
    }
}

beforeUnmount

В данном хуке нужно обязательно:

  1. Отписаться от всех серверных событий;
  2. Отменить и удалить ссылки на Promise (можно будет после задачи);
  3. Удалить из состояния компонента модели и рекорды.

Примитивы и другие объекты, которые лежат в состоянии компонента удалять не нужно.

Изменение состояния компонента

  1. Не нужно перекладывать опции на стейт. Состояние должно вычисляться только если оно вычисляется из значения нескольких опций. В большинстве случае нужно стараться работать с объектом this._options;
  2. Нельзя использовать _forceUpdate. Обновление компонента вызовется автоматически после изменения реактивных свойств (документация).
  3. Не нужно изменять состояние компонента в afterMount, потому что это может вызвать еще одну перерисовку. Корректное состояние компонента должно устанавливаться в beforeMount.

Правильное использование платформенных утилит

При разработки не стоит забывать, что многие платформенные утилиты были переписаны при переходе на Wasaby.

На Wasaby нельзя использовать утилиты из модуля Core. Часть инструментов стали не актуальны в рамках разработки на Wasaby. Большинство остальных заменяются аналогами из модулей Env и Types. Если вы не нашли Wasaby-аналог нужной вам утилиты, можно задать вопросы в группе.

Таблица соответствия старых утилит и их Wasaby аналогов

WS3 УтилитаАналог на Wasaby
Core/UserInfoEngineUser/Info:Info (документация)
Core/RigthsManagerPermission/access (документация)
Core/DateTypes/formatter:date
Core/IoCEnv/IoC
Core/defaultRendersTypes/formatter:number
Core/core-cloneНет аналога