Архитектура Flux приобрела популярность после того, как Facebook принял ее. Это способ управления состоянием компонентов React, чтобы поток данных через приложение был однонаправленным.

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

JSX, система шаблонов, используемая React, позволяет создавать многократно используемые однофайловые компоненты.

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

  • JSX дает четкое представление о структуре DOM, которое более интуитивно понятно, чем несколько строк JavaScript, необходимых для создания той же структуры DOM.
  • Поведение, связанное со структурой DOM - обработчики событий, такие как onClick, onHover, - обрабатываются как функции-члены компонента.
  • Любые изменения в структуре DOM требуют от пользователя вызова setState для изменения состояния компонента вместо прямого изменения DOM. Это упрощает отладку приложения, а также гарантирует, что приложение всегда находится в определенном состоянии.

Однако по мере роста сложности приложения подход Flux также начал проявлять свои ограничения.

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

Хотя в JavaScript можно совместно использовать состояние между различными компонентами / классами с помощью общих переменных или, предпочтительно, паттерна «Наблюдатель», по мере увеличения количества компонентов становится все труднее поддерживать приложение.

Простота реагирования компонентов на изменения состояния смешивается со сложностями объектно-ориентированного проектирования.

Диаграммы - почему их сложно построить?

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

Однако диаграммы отличаются в одном ключевом аспекте: разработчики не рассматривают SVG как DOM. Технически тег <svg> даже не является HTMLElement, как другие элементы DOM, и находится в отдельном пространстве имен. SVG известен только своей способностью масштабироваться до любого размера области просмотра и поддерживать разрешение изображения на постоянном уровне. Это степень, в которой об этом знает большинство разработчиков.

Кроме того, теги, используемые для создания изображения SVG, такие как <point>, <rect /> и <polyline />, очень "математически похожи". Это заставляет разработчиков уклоняться от того, как на самом деле работают структуры SVG.

Даже те, кто работает с приложениями, интенсивно использующими SVG, обычно не знают о его внутренней работе. Они используют другие библиотеки, такие как snap или d3, чтобы избежать проблем с пониманием того, что происходит под капотом.

Избегая базовой сложности тега SVG, можно легко моделировать сложные конструкции SVG.

Геометрия

Рассмотрим, например, гистограмму.

Мы традиционно применяем подход «формочки для печенья» и разбиваем диаграмму на части:

  • ось абсцисс
  • ось Y
  • бары

Опытный разработчик заметил бы, что слово «ось» было написано дважды в приведенном выше списке. Итак, давайте создадим уровень абстракции с именем Axis, от которого могут наследовать подклассы.

Для визуализации полос мы можем создать отдельный класс с именем Bar, который использует масштаб, предоставляемый axis классом. Поскольку диаграммы бывают разных форм, имеет смысл иметь уровень абстракции под названием Geometry , от которого могут наследовать другие классы, а именно Bar, Point, Line и Area. По мере создания более сложных диаграмм можно добавлять несколько новых типов геометрии для визуализации различных типов диаграмм.

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

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

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

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

Перекрестная связь между диаграммами еще больше усугубляет эту проблему. Оркестровка изменений состояния охватывает несколько диаграмм / взаимодействующих компонентов.

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

Это была проблема, которая преследовала и фреймворки разработки пользовательского интерфейса, пока разработка веб-приложений с использованием единого источника истины не стала стандартом. Самой влиятельной библиотекой в ​​направлении перехода к веб-приложениям с единым источником истины был Redux.

Примечание. В следующем разделе объясняется, как использование Redux упрощает разработку веб-приложений. Не стесняйтесь пропустить это, если вы уже знаете о Redux.

Redux

Redux - это библиотека, разработанная Дэном Абхрамовым. Это помогает облегчить бремя разработчиков, предоставляя простой способ поддерживать состояние приложения.

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

Каждое действие определялось уникальным перечислением, которое регистрировалось каждый раз, когда изменение фиксировалось в хранилище состояний. Это упрощало отслеживание того, как изменяется хранилище состояний.

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

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

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

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

Успех модели React-Redux породил ряд других библиотек, таких как Vue и Cycle, а также несколько других реализаций хранилища состояний, таких как MobX и Vuex.

Подробнее о SVG

SVG означает масштабируемую векторную графику. Тег svg может дополнительно содержать различные виды геометрии, которые предоставляют ряд атрибутов DOM.

Круг: <circle />

Атрибуты:

  • cx: x смещение круга в области просмотра
  • cy: смещение круга в области просмотра по оси Y
  • r: радиус круга

Полилиния: <polyline />

Атрибуты:

  • points: массив точек (x, y), через которые проводится линия.

Многоугольник: <polygon />

Атрибуты:

  • points: массив точек (x, y) для построения многоугольника.

Текст: <text />

Атрибуты:

  • x: x смещение текста в области просмотра.
  • y: смещение текста в области просмотра по оси y.
  • innerText: отображаемый текст.

В стандарте SVG доступно гораздо больше типов геометрии, но для целей диаграмм будет достаточно вышеизложенного. Эти геометрические элементы также можно стилизовать с помощью обычного CSS.

В поисках моста

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

  • веб-приложения состоят из узлов DOM. Диаграммы состоят из геометрии SVG.
  • веб-приложения можно разбить на повторно используемые разделы модели DOM, которые можно смоделировать как компоненты. Диаграммы не моделируются как набор геометрических фигур многократного использования.
  • Фреймворки веб-приложений всегда связаны с механизмом создания шаблонов, поэтому структура DOM может быть смоделирована в разметке, а поведение может быть отделено от нее и написано на JavaScript. Диаграммы не имеют такой основы.
  • Фреймворки веб-приложений позволяют включать хранилище состояний с помощью подключаемого модуля. Диаграммы обычно моделируются как компоненты с отслеживанием состояния.

Изменение сложности диаграммы

Диаграмма - это визуальный инструмент, который демонстрирует различия между полями данных с помощью геометрии.

Так как же это работает?

Что мы видим на диаграмме выше? Смещение кругов в области просмотра на основе полей, имеющихся в данных.

Что еще?

  • Смещение тиков внизу в зависимости от поля в данных.
  • Текстовые метки смещены по низу в зависимости от поля в данных.
  • То же, что и выше, в левой части диаграммы.

Разберемся на уровне геометрии.

Как мы визуализируем круги на диаграмме рассеяния?

<circle cx=”horsepowerScale()” cy=”milesPerGallonScale()” cr=”const” />

А как насчет топоров? Оси X: текст + отметки

<text x=”horsepowerScale()” y=”0”>{{ text value }}</text>

<tick x=”horsepwerScale()” y=”0” />

Для оси Y существует аналогичная структура SVG, за исключением того, что функция масштабирования изменяется, а поля x, y инвертируются.

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

  • каждая геометрия в пространстве имен SVG предоставляет визуальные атрибуты
  • значение этих атрибутов привязано к вычисленному значению
  • расчетное значение зависит от шкалы
  • масштаб зависит от поля в данных и диапазона

Что такое шкала?

Масштаб - это функция, которая сопоставляет данные с положением в области просмотра.

Что нужно для масштабирования?

  • область поля
  • длина области просмотра для сопоставления

Пусть R будет длиной области просмотра, а D будет областью данных.

  • Затем мы можем определить функцию масштабирования S как:
  • S = f(D, R) + b

где b - постоянная.

Сколько масштабов должно быть у диаграммы?

Если вы думаете о двоих, то ошибаетесь.

Масштаб существует не только по осям x и y. Сами оси присутствуют на диаграмме только как визуальные привязки, поэтому пользователи могут выстраивать варианты данных по нескольким параметрам.

Ось - это просто геометрия, отображаемая в масштабе.

Сколько существует измерений?

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

Общая концепция состоит из двух простых терминов: Геометрия и Масштаб.

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

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

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

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

Поле Horsepower используется для создания функции масштабирования с именем horsepowerScale().

Поле Acceleration используется для создания функции масштабирования accelerationScale().

Поскольку мы не меняем размер кругов, требуются только две функции масштабирования.

Любой кружок i на диаграмме рассеяния может быть представлен как

<circle cx="horsepowerScale(ti)" cy="accelerationScale(ti)" cr="5" />

где ti - это i-й кортеж в Datatable.

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

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

Это приведет к так называемой «пузырьковой диаграмме».

Грамматика графики

Это похоже на подход Грамматика графики (GOG), где каждая диаграмма определяется меткой (геометрией) и визуальными кодировками, используемыми меткой.

В подходе GOG диаграмма рассеяния будет представлена ​​как:

{
    mark: 'circle',
    encoding: {
        x: 'horsepower',
        y: 'acceleration'
    }
}

Обратите внимание, что существует взаимно однозначное соответствие между кодировкой геометрии GOG и визуальными атрибутами, предоставляемыми геометрией в SVG.

Ось также может отображаться аналогично:

  • Ось x - это галочка с его атрибутом смещения по оси x, привязанным к horsepowerScale(), и смещением по оси y, установленным на 0.
  • Ось Y представляет собой галочку с ее атрибутом смещения по оси y, привязанным к accelerationScale(), и смещением по оси x, установленным на 0.

Чтобы отобразить диаграмму рассеяния со всеми ее элементами, будет достаточно следующего фрагмента кода:

Разложение диаграмм на ассоциацию между визуальными атрибутами и функцией масштабирования позволяет нам просматривать диаграмму как веб-приложение.

Инфраструктура веб-приложений моделирует пользовательский интерфейс как функцию состояния.

Платформы диаграмм должны моделировать геометрию в зависимости от масштаба.

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

  • Первоначально в качестве входных данных предоставляются табличные данные.
  • Для каждого поля в массиве данных создается функция масштабирования. Функция масштабирования выборочно пересчитывает значения, когда поле в столбце привязано к изменениям. Одна и та же функция масштабирования присутствует во всем приложении.
  • Каждая геометрия моделируется как компонент, демонстрирующий визуальные атрибуты.
  • Значение этих визуальных атрибутов привязано к функции масштабирования, которая реагирует на изменения данных.
  • Коллекции геометрии могут быть представлены в разметке с использованием выбранного механизма создания шаблонов, например, hyperHTML, усов или ручек. В идеале механизм шаблонов должен быть представлен как плагин, чтобы мы могли избежать написания привязок для разных библиотек, таких как React и Angular.
  • Хранилище состояний, которое выборочно вычисляет масштабы, также должно быть представлено как плагин.

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

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

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

Обратите внимание на разделение механизма шаблонов и хранилища состояний от фактической логики рендеринга.

Конечные точки

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

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

Пользователи могут выбирать между React, Preact, lit-html или расширять интерфейс Renderer для написания своего собственного. Средство визуализации по умолчанию просто изменяет DOM напрямую.

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

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

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

В примере точечной диаграммы для каждой группы кругов, которые визуализируются, соответствующие участки геометрии оси x / y также должны отображаться одновременно.

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

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

Мы также можем разбить расчет значений визуальных атрибутов.

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

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

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

0-е значение для визуального атрибута V, привязанного к полю 0 N, можно рассчитать следующим образом:

V(0) = S(d0, R) + b0

  • где d0 - это 0-й кортеж данных из таблицы данных
  • R - это диапазон, поддерживаемый компонентом.
  • b0 постоянный

Если мы вместе напишем серию таких уравнений, мы увидим следующее:

V(0) = S(d0, R) + b0

V(1) = S(d1, R) + b1

V(2) = S(d2, R) + b2

..

V(m) = S(dm, R) + bm

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

Как так?

Вышеупомянутое расположение подозрительно похоже на матрицу.

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

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

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

Можно с уверенностью сказать, что нам нужен более надежный, доступный и удобный способ разработки визуализаций.

Как вы думаете?