Что такое meta application?

Определимся сразу, что мета-приложения и мета-компоненты - это ещё не устоявшиеся в индустрии термины, а скорее предложение, которое может быть принято или отвергнуто сообществом. Самое время объяснить, что конкретно мы имеем в виду.

Meta applications - относительно независимые решения в основной структуре веб-приложения, такие как:

  • Виджеты

  • Микро-фронтенды

  • Элементы UI-библиотек

  • Приложения-надстройки

  • и так далее...

Это всё приложения или компоненты, решающие свою выделенную задачу, или часть более общей задачи, которые могут быть простыми или довольно сложными сами по себе, и существуют относительно независимо от архитектур и принципов разработки хост-приложений. Упс, еще один новый термин. Ну тут должно быть проще: host application - это “среда” для мета-приложения, окружение интеграции.

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

Формулируем задачу

Существует простой и распространенный подход к интеграции виджетов: мы даем указать, через созданный нами API, элемент-контейнер, в который и добавляем всё необходимое. И все у нас хорошо, пока не выяснится, что пользователь-интегратор - использует стили, которые глобально влияют на все элементы определенного типа на странице... И вот, вы должны позаботиться о защите CSS, но таким образом, чтобы дизайн вашего решения можно было настроить снаружи каким-то вменяемым способом... А потом, клиенту потребуется использовать несколько виджетов, на одной странице и с разными настройками одновременно... А потом, вы выясните, что цикл рендеринга хост-приложения влияет на то, как ваш виджет инициализируется, и вам нужно будет объяснить пользователю, как и когда вызывать методы вашего API для того, чтобы не возникало никаких "гонок" и прочих "cannot read property X of undefined". А ещё, вам может понадобиться отобразить одну часть интерфейса в одном месте хост-приложения, а другую часть - в другом, и в разное время... В общем, никакие классические и консервативные подходы, достойных ответов, на эти вопросы, не дают.

Подытожим. Для создания виджета, нам необходимо:

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

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

  • Решить то, каким образом виджет может быть настроен, какие настройки могут быть общими, и как предоставить каждому экземпляру виджета на странице его персональные и уникальные настройки. И желательно, чтобы это было возможно сделать в разных экосистемах: от динамических JS-приложений до статических HTML-документов.

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

  • Постараться оставить решение максимально "лёгким", с вниманием отнестись к потреблению ресурсов.

Путь к решению

Луч света в темном царстве виджетостроения - это стандарты Custom Elements и Shadow DOM.

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

Shadow DOM дает нам возможность надежно защитить наши стили и используемые в JS-коде селекторы от "протечек", как извне, так и наружу, что тоже очень полезно.

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

В принципе, мы можем использовать любую привычную нам библиотеку для разработки "тела" виджета, и потом обернуть результат в Custom Element c Shadow DOM. На этом этапе, стоит вспомнить о производительности и трафике: будет не очень хорошо, если мы используем что-то громоздкое - наши пользователи могут воспринять это негативно если для них важна эффективность и общий объем загружаемого кода. Вы, наверное, догадываетесь к чему я клоню: React или Vue, как варианты, отпадают первыми - слишком много оверхеда (~ 40K и ~ 20K gzip/br соответственно). Про Angular я вообще молчу. Давайте обозначим рамки: у нас есть кандидаты, чей добавляемый к общим цифрам размер, вписывается в ~3-5К gzip, вот на них и обратим свой пристальный взор.

Из довольно популярных, и при этом, "лёгких" вариантов, можно отметить Svelte и Preact. В экосистемах этих библиотек есть готовые решения для использования с Custom Elements, что добавляет плюсов в их копилку. Но и тут есть свои нюансы: например, не для всех приемлема зависимость от компилятора в Svelte. Вообще всё, что использует свои специфические компиляторы отпадает, если вы хотите иметь возможность гибко работать с кодом компонентов в хост-приложении напрямую (они должны уметь собираться как обычный JS). И реализация, с использованием сразу двух, принципиально разных компонентных моделей (уровень библиотеки и Custom Elements) - меня лично, очень смущает, как с архитектурной, так и с концептуальной точки зрения. Поясню: в случае, если мы создаем решение, сочетающее в себе некий набор элементов - каждый такой элемент придется "оборачивать" отдельно, и затем разбираться уже с двумя типами компонентов: внутренними и теми, которые мы отдаем для пользовательской интеграции. Это неудобно и, в определенный момент, может выйти нам боком. Идем дальше.

Далее, мы подходим к набору библиотек, специально предназначенных для работы со стандартом Custom Elements, у которых поддержка реализована на уровне генов, где компонент приложения = Custom Element.

Одна из самых популярных и подходящих под наши цели среди них - это LitElement, от разработчиков из Google. В свое время, я делал выбор именно в её пользу. Тогда, основные проблемы пришли со стороны поддержки Content Security Policy. Дело в том, что LitElement использует для подключения стилей экспериментальный интерфейс adoptedStyleSheets, который, на текущий момент, не поддерживается в Safari. И, в случае, если поддержки нет, LitElement создает в shadowRoot компонента, тег <style> куда добавляет CSS в виде текста, что, как вы уже, наверное, догадались, конфликтует с настройками CSP в общем случае... Увидеть проблему можно сравнив результат работы LitElement на https://lit.dev/playground/ с помощью инструментов разработчика в Safari и Firefox/Chrome. Использовать стратегию nonce или hash мы не можем, поскольку наше решение - встраиваемое. Говорить своим пользователям, о том, что им необходимо добавлять в настройки флаг unsafe-inline мы посчитаем дурным тоном, и начнем думать о том, как решить вопрос иначе.

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

Кстати, для тех, кто задастся вопросом, чего я так прицепился к этим CSP, я покажу вот этот график: https://trends.builtwith.com/docinfo/Content-Security-Policy.

С опытом, мы начинаем видеть и другие недостатки LitElement:

  • Обработка шаблонов через механизм Tagged templates - это привязывает определения шаблона к контексту класса компонента и затрудняет манипуляции с шаблонами, даже на уровне примитивного разделения кода.

  • Резолвинг модулей через node.js, вместо, поддерживаемых браузерами, относительных путей, что, в режиме разработки, привязывает нас к специальному серверу и не дает использовать компоненты "на лету" в по настоящему "сыром" виде. Да, мы знаем про import-map, как и про то, что это нигде, кроме движков, основанных на Chromium, нативно не поддерживаются.

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

  • Общее движение от близости к нативным API в сторону усложнения: с каждым обновлением документации ты превращаешься в "LitElement-разработчика", хотя желал простоты и дзена.

В любом случае, все эти проблемы, так или иначе, решаемы. И для каждой, скорее всего, найдется даже сразу несколько возможных направлений поиска решений. Но, тут всплывает резонный вопрос: если мы уже начали лепить надстройки и кастомизации для нашей базовой библиотеки, почему бы не перестать бороться с ветряными мельницами, и не создать "уницикл", который будет ИЗНАЧАЛЬНО ПОЛНОСТЬЮ СООТВЕТСТВОВАТЬ нашим нуждам?

Решение

Итак, встречайте: Symbiote.js - библиотека, специально созданная для мета-приложений.

Как следует из названия, Symbiote - это про симбиоз. Для того, чтобы симбиоз стал возможен, при разработке мы следовали принципу максимального приближения к веб-платформе и её нативным API, при сохранении достаточного уровня удобства. Symbiote.js - изначально предназначен для создания приложений со слабосвязанной архитектурой, которая упрощает интеграцию в широкий набор сред и окружений. Symbiote.js дает высокий уровень свободы, что, конечно же, подразумевает и высокий уровень ответственности. Несмотря на общую внешнюю схожесть (динамические привязки данных в шаблонах, состояния, жизненный цикл), Symbiote.js довольно сильно отличается от своих более известных коллег концептуально. И всё дело именно в этих отличиях:

  • Шаблоны - это HTML. Буквально, шаблоном в Symbiote.js считается то, что браузер может сам преобразовать в объектную модель без ошибок и дополнительных действий с нашей стороны. Синтаксис шаблонов основан на HTML-тегах и их атрибутах, доступных для пост-обработки через самый обычный DOM API. Таким образом, в отличие от JSX, или даже lit-html, шаблоны могут представлять из себя обычные шаблонные литералы (строки) или отдельные HTML-файлы (если вы используете HTML-лоадер для вашего сборщика).

  • Данные определяются контекстом. В Symbiote.js, из коробки, есть поддержка как работы с локальными данными компонента, так и с данными из общедоступных контекстов: абстрактных (named) или сформированных с учетом положения компонента в DOM-дереве хост-приложения. Инициализация компонентов происходит после того, как они попадают в DOM, и, образно говоря, первым делом Symbiote-компонент задает вопросы: "так, куда я попал?" и "кто вокруг меня?"

  • Pub/sub - работа с данными реализована через простейший, как для понимания, так и в использовании, паттерн.

  • Shadow DOM - это опция, выключенная по умолчанию. Теневой документ - это очень мощный инструмент, дающий виджетостроителю многое. Но иногда, он-же является и источником проблем. В концепции Symbiote.js - Shadow DOM - это важная, но далеко не обязательная часть. Лично я, предпочитаю создавать теневой DOM только на "внешних рубежах", не усложняя стилизацию внутри компонентов и не добавляя лишней работы браузеру.

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

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

  • Поддержка объектной модели документа. Symbiote.js не создает искусственных барьеров между DOM и вашим кодом в компонентах, а напротив, предоставляет прямой и удобный доступ.

  • Прогрессирующая сложность. Согласно концепции "Progressive Complexity", простые задачи должны иметь такое-же простое решение. А для решения задач сложных, не должно быть никаких концептуальных либо архитектурных ограничений. И в Symbiote.js всё именно так.

  • "HTML as low-code". Симбиот стимулирует перенос взаимодействий компонентов с окружением на уровень HTML и CSS, туда, где за всё отвечает браузер, а не какая-либо, специфичная для конкретного стека, js-абстракция. Симбиот позволяет строить мощный и гибкий API на уровне самых базовых сущностей платформы.

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

  • Технологический агностицизм в генах. Это касается и экосистем и рантайма. Это касается и внешних зависимостей, которых у Symbiote.js - нет.

Примеры кода

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

Для подсветки HTML и CSS синтаксиса внутри шаблонных литералов, вы можете использовать специальное расширение для вашей IDE, например это: https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html

Простой пример, не требующий установки:

<script type="module">
  import { BaseComponent } from 'https://symbiotejs.github.io/symbiote.js/core/BaseComponent.js';

  class MyComponent extends BaseComponent {
    init$ = {
      count: 0,
      increment: () => {
        this.$.count++;
      },
    }
  }

  MyComponent.template = /*html*/ `
    <h2>{{count}}</h2>
    <button set="onclick: increment">Click me!</button>
  `;

  MyComponent.reg('my-component');
</script>

<my-component></my-component>

Этот код можно просто скопировать в HTML-файл и открыть в браузере.

Более сложный пример, где есть динамический рендеринг таблицы и общий Shadow-контейнер:

import { BaseComponent } from 'https://symbiotejs.github.io/symbiote.js/core/BaseComponent.js';

//// Dynamic list item component:
class TableRow extends BaseComponent {}

TableRow.template = /*html*/ `
  <td>{{rowNum}}</td>
  <td>Random number: {{randomNum}}</td>
  <td>{{date}}</td>
`;

TableRow.reg('table-row');

//// Dynamic list wrapper component:
class TableApp extends BaseComponent {
  
init$ = {
  tableData: [],
  buttonActionName: 'Generate',
  generateTableData: () => {
    this.$.buttonActionName = 'Update';
    let data = [];
    for (let i = 0; i < 1000; i++) {
      data.push({
        rowNum: i + 1,
        randomNum: Math.random() * 100,
        date: Date.now(),
      });
    }
    this.$.tableData = data;
  },
}

TableApp.shadowStyles = /*css*/ `
	table-row {     
		display: table-row;
 	}
 	td {     
 		border: 1px solid #f00;
 	}
`;

TableApp.template = /*html*/ `
  <button set="onclick: generateTableData">{{buttonActionName}} table data</button>

  <table
    repeat="tableData"
    repeat-item-tag="table-row">
  </table>
`;

TableApp.reg('table-app');

Пример с определением шаблона в разметке за пределами компонента:

<script type="module">
  import { BaseComponent } from 'https://symbiotejs.github.io/symbiote.js/core/BaseComponent.js';

  class MyComponent extends BaseComponent {

    // Enable external template usage:
    allowCustomTemplate = true;

    init$ = {
      title: 'Title',
      clicks: 0,
      onClick: () => {
        this.$.clicks++;
      },
    };
  }

  MyComponent.reg('my-component');
</script>

<template id="external-template">
  <h1>{{title}}</h1>
  <div>{{clicks}}</div>
  <button set -onclick="onClick">Click me!</button>
</template>

<my-component use-template="#external-template"></my-component>

Обратите внимание, что в первом и последнем примерах, обработчики нажатий на кнопку привязываются к шаблону по разному: Symbiote.js поддерживает два типа синтаксиса привязок, каждый из которых более удобен в определенных случаях. О том, почему это так, я более подробно расскажу в одном из следующих материалов.

Symbiote.js поддерживает TypeScript и может одинаково свободно использоваться как в TypeScript, так и в JavaScript проектах.

Заключение

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

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

В общем, ждем вас в/на GitHub, будем благодарны за "звездочки" и любую активность в обсуждениях. Всем - добра и мира.

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


  1. Synopticum
    16.08.2022 13:21

    Привет, одно время тоже интересовался темой микрофронтендов без рантайма, и в итоге ничего нормального так и не нашел. Правда я решал немного другую задачу — нужно было сделать максимально независимый от зависимостей на внешние библиотеки UI kit, который должен был одинаково работать и выглядеть на любых страницах одного и того же сайта, состряпанного из наслоений различных технологий года так с 2004-го. Сделал в итоге на базе core-бандла React + на нем же независимых микрофронтендов вплоть до отдельных компонентов-бандлов по типа Svelte.

    Все эти чисто нативные решения без дев-сервера, и т.д. и т.п. упираются в то, что код писать на них тупо неудобно.

    Вот еще несколько моментов:

    1. Сервер все равно есть всегда, без сервера тупо не работают многие вещи по причинам безопасности. Почему бы и не быть дев-серверу. Собственно, в Polymer/lit довольно быстро дошли до этой мысли и последние пару лет dev-сервер там есть как и этап сборки.

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

    2. Ты говоришь, что шаблонизатор HTML, но это не так, это видно по первому же примеру:

    MyComponent.template = /*html*/ `
        <h2>{{count}}</h2>
        <button set="onclick: increment">Click me!</button>
      `;
    


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

    3. Синхронные обновления DOM без батчинга там где он нужен = привет тормоза.

    4. BaseComponent это тот же рантайм. Хотя здесь вижу плюс, что он маленький и загружается один раз засчет кеширования импортов.

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


    1. i360u Автор
      16.08.2022 14:09
      +1

      Вы как-то все неверно поняли.

      1. Сервер конечно нужен, но сервера бывают очень разные. Бывают сервера такие, как на GitHub pages, где модули резолвятся только по относительным путям или через https. Бывают локальные дев-сервера, где для хот-релоада добавляются какие-то сущности в ваш документ, которые вам могут мешать в определенных ситуациях (например, когда вы гоняете тесты в Puppeteer). Бывают сервера, решающие другие специфические задачи, как в случае с LitElement. Никто не предлагает отказаться от серверов и удобства. Просто с Symbiote - у Вас все будет работать во ВСЕХ перечисленных случаях, он - серверный агностик. В отличие от LitElement. Кто Вам не дает использовать любые пост/препроцессоры я тоже не очень понял.

      2. Это именно HTML, потому, что браузер это парсит как HTML. Наличие меты для постобработки не делает шаблон чем-то принципиально иным. Еще раз уточню: сначала создается участок DOM, именно в стандартном объектном представлении, а потом обрабатывается как DocumentFragment. НЕ шаблон. Именно поэтому, это не шаблонизатор, во всяком случае, в общепринятом представлении.

      3. Никто не отменяет батчинг там, где он нужен. А вот использование батчинга там, где он не нужен - это и есть "привет тормоза". Вообще, мы очень внимательно следим за производительностью и гоняем бенчмарки. Symbiote вносит обновления быстрее чем те либы, которые батчинг используют безальтернативно, это просто факт. Про бенчмарки я тоже планирую написать, в одну статью всего не засунешь.

      4. BaseComponent, наверное, можно назвать рантаймом, с натяжкой. Но это не непрозрачная абстракция, как у других.

      В остальном все хуже.

      Кроме перечисленного, еще какие-то аргументы есть?

      Настолько ли даже пара метров core-бандла какого нибудь фреймворка плохи, чтобы отказываться от на порядок более удобного удобства разработки?

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

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


      1. Synopticum
        16.08.2022 14:45

        Я разобью ответ на два комментария. Первый про часть техническую.

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

        1. Про сервер я писал в ответ на следующее заявление: «Вы можете писать свой код и сразу видеть результат в браузере, без установки каких-либо специфических зависимостей: компиляторов, специальных dev-серверов и прочего». Я говорю, что не будет такой ситуации, когда описанное будет преимуществом. Звучит красиво, но без этапа сборки писать код неудобно, без дев-сервера в большинстве ситаций просто невозможно — работа с JS через файловую систему налагает кучу ограничений. Подобные рекламные заявления делали разработчики Polymer в свое время, но больше не делают по описанной выше причине.

        Ну а если появляется этап сборки и дев-сервер, то у React/Vue и прочей братии тут все гораздо лучше в плане обвеса и возможностей.

        2. Я понимаю, что этот выбор сделан чтобы уменьшить нагрузку на js-рантайм, т.к. такой шаблон действительно парсится нативно браузером, но повторю свое мнение — это крайне снижает удобство разработки. Например вот это «set='onclick: increment'» если я правильно понял, вынуждает передавать название обработчика в виде строки, что убивает рефакторинг и кучу функуционала в IDE. С точки зрения разработчика это именно то, что я написал в первом комменте — примитивный шаблонизатор.

        3. По производительности действительно надо смотреть и тестить, тут согласен.

        4. Да такая же как и везде, просто меньше по объему и проще. За что приходится платить DX как у фреймворков допотопного поколения.

        Кроме перечисленного, еще какие-то аргументы есть?


        Поштучно их много, да тот же Shadow DOM — он не только изолирует стили, но и затрудняет доступ к глубоко вложенным элементам формы извне и мешает SSR. Но мой основной посыл не в этом, а в том что сама эта концепция — максимальное стремление к нативности и отсутствию зависимостей — это путь в никуда. Плюс будет один (в весе), а минусов сколько фантазии хватит.

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

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


        Я так решил, потому что посмотрел на примеры кода, а также потому, что я писал крупный проект на lit, которым вы точно вдохновлялись, ну потому что пробовал на живых проектах много других фреймворков — React, Angular, Vue и мне есть с чем сравнить.


        1. i360u Автор
          16.08.2022 17:15
          +1

          Инжиниринг - это искусство компромиссов. Мы всегда платим чем-то за что-то. Практически всегда у нас либо "толстая броня", либо "высокая мобильность". Мы все все свои решения взвешиваем на весах. Все эти компромиссы определяются контекстом и теми рамками, в которых ваше решение должно работать.

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

          Фишка симбиота не в том, что он лучше или хуже React или Vue и представляет какую-то альтернативу их развитым экосистемам, а в том, что он может дружить с Vue и React и всеми остальными одновременно. Вы можете рендерить шаблоны Симбиота прямо в Реакте. И на сервере. Вы можете создавать API, для работы с которыми, вашим пользователям не нужно вообще писать ни строчки на JS. И это будет работать и в Wordpress, и с Jamstack и в других местах.

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

          Если вернутся к более предметному обсуждению, то опять же, Вы пишите про трудности работы с Shadow DOM, при том что именно этот же аргумент использовал и я. Как можно записать это в недостатки, если в Симбиоте нет Shadow DOM по умолчанию (включается флагом, только при необходимости)?

          Ну и так далее, вы рубите с плеча не разобравшись.


          1. Synopticum
            16.08.2022 17:47

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


            Так я привел несколько конкретных примеры в комментах без всякой аппелляции к опыту — не реально работать без этапа сборки и дев-сервера, отсутствие поддержки чего либо кроме строк при передаче через аттрибуты HTML, отсутствие внятных плюсов при реализации UI-библиотеки. И не получил на них ответа.

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

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

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

            Да, точно также работают и веб-компоненты, которые использует ваша библиотека. Разница лишь в том, что в вашем случае часть забот берет на себя браузер.

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

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

            А потом, вы выясните, что цикл рендеринга хост-приложения влияет на то, как ваш виджет инициализируется, и вам нужно будет объяснить пользователю, как и когда вызывать методы вашего API для того, чтобы не возникало никаких «гонок» и прочих «cannot read property X of undefined».

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

            А ещё, вам может понадобиться отобразить одну часть интерфейса в одном месте хост-приложения, а другую часть — в другом, и в разное время...

            Порталы в реакте, аналогичный функционал в других фреймворках.

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

            Все это есть в любом современном фреймворке.

            В общем, никакие классические и консервативные подходы, достойных ответов, на эти вопросы, не дают.

            В свете вышенаписанного, совершенно неочевидный вывод. В итоге непонятно, какая задаче решается.

            Если речь про фреймворко-независимые компоненты в гибридных приложениях, где намешаны React/Vue/прочие ангуляры, то вроде идея и красивая — наделаем на Symbiote компоеннтов, а в других частях обернем их во врапперы и все будет красиво. А на деле это будет +1 технология в проекте.

            Если на проекте есть React, то можно сделать тоже самое на React, нулевой ценой на частях приложения, уже написанных на реакте, и ценой core-бандла + самих standalone-компонентов на реакте, опирающихся на этот core, на остальных частях приложения. Вместо реакт подставьте любую другую технологию _уже_ использующуюся на проекте.

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

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


            1. i360u Автор
              16.08.2022 18:18
              +1

              Так я привел несколько конкретных примеры в комментах без всякой аппелляции к опыту — не реально работать без этапа сборки и дев-сервера, отсутствие поддержки чего либо кроме строк при передаче через аттрибуты HTML, отсутствие внятных плюсов при реализации UI-библиотеки. И не получил на них ответа.

              Я вроде ответил. Вы делаете предположения исходя из каких-то неверных предпосылок, а я пытаюсь понять откуда они берутся.

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

              Использование строк удобно именно тем, что это строки. У вас есть широчайшие возможности по манипуляции ими. Это самый гибкий подход. Опять же, не совсем понимаю, в чем именно вы видите проблему. Лично у меня эти шаблоны вызывают меньше проблем, чем lit-html или JSX. И не только у меня.

              Да, точно также работают и веб-компоненты, которые использует ваша библиотека. Разница лишь в том, что в вашем случае часть забот берет на себя браузер.

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

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


      1. Synopticum
        16.08.2022 14:57

        Второй — про юзкейсы:

        • Виджеты
        • Микро-фронтенды
        • Элементы UI-библиотек
        • Приложения-надстройки



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

        Если это не так, а например для кейса «Элементы UI-библиотек» это не так, то у вас _будет_ отдельный core-бандл и никуда вы не денетесь и будете его тащить, либо будете дублировать пачки кода и стилей в виде копипаста от компонента к компоненту.

        Для приличного размера дизайн-системы эти бандлы могут быть довольно большими, частая задачка которая мне попадается — выпилить material ui (у которого прям отдельный core-бандл в npm без котоого ничего не работает) или что-то подобное, и заменить более легковесным. Так вот даже если менять, все равно сам создаешь такой же бандл, просто уже меньше, и под нужды заказчика.

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

        Далее кейс «Микро-фронтенды» — полагаю они отличаются от виджетов какой-то повышенной сложностью, возможно наличием роутинга. В общем без пояснения данный пункт не понятен, но выглядит как то что тут тоже будет отдельный рантайм для управления микрофронтендом. Просто меньше чем у того же реакта. Но и неудобнее.

        По кейсу «Приложения-надстройки» тоже нужно поясление, не знаю что это такое.

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


        1. i360u Автор
          16.08.2022 17:48
          +2

          предлагает нативность которая непонятно что дает

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

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


          1. Synopticum
            16.08.2022 17:53
            -2

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

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


            Можно. А можно и использовать возможности какого-нибудь нормального бандлера. Заодно можно будет не париться с относительными путями при написании кода и рефакторинге.

            Ну и остальные перечисленные замечания вы пропустили. Вернусь в ветку, если появится что-то по существу.


  1. DeusX
    16.08.2022 19:05
    +1

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


    1. i360u Автор
      16.08.2022 19:06
      +1

      Спасибо. Над доками работаем.