Pull to refresh

Comments 27

UFO just landed and posted this here

Да, конечно, я об этом как раз и пишу. Есть следующие варианты:

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

  • использовать отдельный HTML-файл и грузить шаблон по необходимости, только если браузер встретил ваш кастомный тег и инициализировал его

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

  • использовать внешний JS-файл с шаблоном в виде экспорта шаблонного литерала

  • Использовать внешнюю декларацию шаблона с уникальным идентификатором, и далее использовать его в компоненте с помощью флага allowCustomTemplate и атрибута use-template . В этом случае (как и в первом примере из списка), вы можете определять свой шаблон для каждого конкретного применения вашего тега индивидуально, что дает огромную гибкость

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

UFO just landed and posted this here

В общем случае, можно создавать абстрактный стейт-контекст, доступный для всех компонентов, как, например, тут: https://symbiotejs.org/2x/playground/l10n/

При инициализации, каждый компонент сможет автоматом получать актуальное значение, вносить изменения и использовать прямые привязки данных в шаблонах. Если нужно что-то более сложное, с очередями, версионированием либо графовыми структурами - можно написать небольшую обвязку поверх базового интерфейса PubSub Но это если нужно действительно что-то сложное, в основной массе случаев, достаточно базовых возможностей работы с контекстами: https://symbiotejs.org/2x/docs/Context/

UFO just landed and posted this here

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

Тут стоит оставить disclaimer, что вы являетесь автором этой библиотеки

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

Давайте будем называть вещи своими именами. У вас нет встроенной ИИ-функции, а есть запрос к внешнему сервису. А это не HTML-тег, а кастомный компонент на вашем фреймворке. По сути, вы продемонстрировали только то, что из JS-кода можно отправить HTTP-запрос, и это не выглядит особо удивительным.

Ок, давайте.

  1. С каких пор кастомный тег перестал быть тегом? В любой разметке он присутствует и запускает цикл своей инициализации именно как тег, какая вообще разница с помощью какого фреймворка он создан? Custom Elements - это полноправная часть современного DOM API и веб-стандартов. И в моем примере это именно полноправный тег, который парсится браузером как часть общего HTML и представлен в DOM как полноценный элемент.

  2. Почему встроенная ИИ-функция, которая работает через запрос к внешнему сервису, перестала, вдруг, быть встроенной? HTML-тег, в моем примере, представлен именно как возможная точка интеграции ИИ на уровне базовых примитивов веба. Как, по вашему, должна была выглядеть ПО НАСТОЯЩЕМУ встроенная ИИ-функция?

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

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

Моя претензия в первую очередь к желтому заголовку. Вы намеренно используете по соседству слова "HTML-тег" (что-то очень простое) и "встроенная ИИ-функция" (что-то очень сложное), чтобы на этом контрасте создать иллюзию удивительности и парадоксальности. При прочтении оказывается, что парадокса никакого нет, допущения делают задачу тривиальной, а предназначение статьи по всей видимости только в том, чтобы попиарить вашу библиотеку. Но даже 89 ее потенциальным пользователям, судя по количеству звезд на гитхабе, в качестве готового рецепта она не подойдет, потому что чтобы не светить свой ключ от API, большую часть придется переписать.

Настоящий "HTML-тег со встроенной ИИ-функцией" на самом деле может существовать. Чтобы оправдывать название, работать он должен так: W3C стандартизует новый атрибут, например <textarea ai-actions="summarize rewrite explain"></textarea>, а разработчики браузеров добавляют в них локальную LLM, которая может эти команды выполнять. Я уверен, что в определенный момент что-то подобное появится, и тогда новость была бы сногсшибательной. Но вы написали совсем не про это.

Моя претензия в первую очередь к желтому заголовку. Вы намеренно используете по соседству слова "HTML-тег" (что-то очень простое) и "встроенная ИИ-функция"

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

Чтобы оправдывать название, работать он должен так: W3C стандартизует новый атрибут, например <textarea ai-actions="summarize rewrite explain"></textarea>, а разработчики браузеров добавляют в них локальную LLM, которая может эти команды выполнять.

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

Но это все отговорки, конечно я виноват. Виноват в том, что написал свою либу, выложил ее в Open Source и упомянул ее в своей статье. Да, грешен. Имел наглость пиарить свою работу, которую искренне считаю лучшим решением для подобного рода задач, по огромному количеству технических и концептуальных причин. Как я вообще посмел лезть к уважаемой аудитории с поделкой, у которой всего 89 звезд на гитхабе, всего 5000 еженедельных скачиваний и которую используют в проде сотни разработчиков в составе других частных решений? Могу я рассчитывать на ваше прощение? Или мне удалить статью и уйти с Хабра?

Не обижайтесь, он просто любит звёздочки. Очень.

А мне очень интересны ваши работы. Пусть не так много звёздочек, и нет огромного комьюнити. Ничего страшного. Даже если бы было 1 скачивание в неделю, все равно бы интересовался.

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

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

Спасибо за публикацию! Подскажите, а как Вы решили проблему изоляции css-стилей в Симбиоте? Используете shadow dom или какой-то другой механизм?

Например, в вашем примере я вижу, что используете имя кастомного тега. А как поступите, если нужно стилизовать не "класс элементов", а определенный инстанс/элемент?

В качестве примера использования иных техник изоляции можно привести css-modules. Пробовали ли его использовать совместно с Симбиотом и esbuild?

При создании компонента в Symbiote.js можно включить режим Shadow DOM специальным флагом (renderShadow), либо определением стилей через интерфейс ShadowStyles. В случае использования интерфейса RootStyles, стили будут определены для любого root верхнего, по отношению к позиции компонента в DOM, уровня. Таким образом, для эффективной изоляции, вы можете создать один общий шедоу-враппер для своего конкретного решения, например, виджета, и использовать свой дизайн-API для его настройки извне, а внутри использовать легковесные компоненты без Shadow DOM с классической схемой стилизации. Ну и можно комбинировать все это с любой другой техникой стилизации, Симбиот это позволяет, поскольку максимально близок к платформе. Стилизовать конкретный инстанс можно так-же, как и любой другой DOM-элемент в дереве, и даже задавать свой отдельный шаблон каждому конкретному инстансу, при необходимости.

Прочёл все Ваши статьи и заметил что с каждой итерацией Symbiote становится всё больше похож на W3View, который существует уже с 2016 года практически без изменений.

Осталось только отказаться от использования CustomElements в качестве основы для внутренней компоновки, ну и пройтись бритвой Оккама по всяким модным двусторонним связываниям - this.ref - вполне достаточен.

В целом этот тренд всецело поддерживаю - HTML, CSS и JS - это всё что нужно для построения приложений любой сложности.

Успехов

Осталось только отказаться от использования CustomElements в качестве основы для внутренней компоновки, ну и пройтись бритвой Оккама по всяким модным двусторонним связываниям - this.ref - вполне достаточен.

CustomElements - это нативное браузерное решение вопроса жизненного цикла компонентов. Совершенно непонятно зачем отказываться от того, что решает тонны проблем без лишних костылей, велосипедов и лишних абстракций. А 2-way биндингов в Симбиоте и так нет, так как я, как и многие другие, считаю это антипаттерном. Но this.ref - точно недостаточно для вменяемого DX, без биндингов вы просто будете писать на порядок больше повторяющегося кода.

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

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

            //hello-world component
			<div as="hello-world">
				<h1 ref="content"></h1>
				<input ref="input" placeholder="type your name here">
				<h2>Hello <span ref="name">Anonimous</span>!</h2>
				<script type="javascript">
					var me=this;
					this.ref.input.oninput = function(e){
						me.setData(me.ref.input.value);
					};
					this.onSetData = function(data){
						me.ref.name.innerText = data || 'Anonimous';
					};
				</script>
			</div>
			
			//root of app 
			<div as="double-hello-world">
				<hello-world>Hello first</hello-world>
				<hr>
				<hello-world>Hello second</hello-world>
			</div>

И никакого повторяющегося кода :)

А CustomElements очень хороши для изоляции стилей и красивой установки виджета, тогда приходилось с этим приседать и наклоняться ;).

Да, нативное всегда лучшее, согласен, но проблема CustomElements в том, что они регистрируются на глобальном уровне и иногда это мешает.

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

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

И никакого повторяющегося кода :)

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

me.ref.name1.innerText = data || 'Anonimous';
me.ref.name2.innerText = data || 'Anonimous';
me.ref.name3.innerText = data || 'Anonimous';
// и так далее

Потом вы будете повторяться со значениями атрибутов, которые тоже бывают привязаны к данным. И ко всем остальным свойствам, которые вам понадобятся. А потом, когда ваш компонент станет действительно сложным, вы НЕИЗБЕЖНО начнете путаться в этих повторениях.

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

Для полноты картины, приведу пример того, как бы выглядел похожий компонент на Symbiote.js:

import Symbiote, { html } from '@symbiotejs/symbiote';

class HelloWorld extends Symbiote {
  init$ = {
    name: '',
    onInput: (e) => {
      this.$.name = e.target.value || 'Anonymous';
    },
  }
}

HelloWorld.template = html`
  <input ${{oninput: 'onInput'}} placeholder="type your name here">
  <h1 ${{'@title': 'name'}}>Hello {{name}}!</h1>
  <h2>Hello {{name}}!</h2>
  <h3>Hello {{name}}!</h3>
  <inner-component ${{'$.someProp': 'name'}}></inner-component>
`;

HelloWorld.reg('hello-world');

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

PS. На виджетах я сам "собаку съел". И даже продолжаю есть. Именно так и появился Симбиот.

О, интересная вещь. Это типа веб-компонент без shadow-dom, но с поддержкой слотов. Я подобное тоже писал. Гляну. Меня только смущает, что используете атрибут as - он всё-таки предназначен для ассоциации тэгов с кастомными элементами. Как бы неожиданное поведение не возникло, хотя внутри template/script не должно возникнуть (надо чекать).

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

Так, по поводу кастомных дата-атрибутов ещё слово не сказал, но окей, вы сами сказали зачем-то.

Вот что пишут: https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes. https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes

Во всех источниках, которые я смотрел, упоминается, что кастомный атрибут в html 5 должен начинаться с префикса data-. Поправьте меня, на всякий случай, если допустил неточность. У меня у самого в моей ui-библиотеке используются data-ref/data-scope атрибуты для получения ссылок на конкретные элементы. Ref выглядит локаничнее, конечно, чем с префиксом data-.

Но меня это не волновало, вообще-то. Я говорил о том, что атрибут as уже заюзан. И я отчасти ошибся. Так, для Custom Elements используется не as, а is (https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#using_a_custom_element) , а атрибут as используется в link (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#as).

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

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

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

С CustomElements точно так-же приходится ручками инициировать и подчищать хвосты.

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

А потом, когда ваш компонент станет действительно сложным,

Я сделаю декомпозицию, - это почти бесплатно ;)

вы НЕИЗБЕЖНО начнете путаться в этих повторениях.

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

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

Опять-же, предпочитаю не привязывать данные к текстовым нодам а использовать <span ref="key">, ну и собрать строку любым способом мне тоже никто не запрещает.

PS. На виджетах я сам "собаку съел". И даже продолжаю есть. Именно так и появился Симбиот.

А я ушёл таки в бэк, ибо там где я сейчас работаю, на фронте безраздельно свирепствует React :)

Лучше избежать проблем, тогда и решать их не придётся.

Ок, даже если так, вы же понимаете, что ваша либа этих проблем никак НЕ избегает? Вы используете синтаксис кастомных тегов, а они проверяются в общем реестре и могут точно также конфликтовать при наличии в общем скоупе других кастомных тегов. Для решения этой проблемы есть такая штука как тег template, но вы его не используете. Как не используете многих других современных стандартов для избежания проблем.

С CustomElements точно так-же приходится ручками инициировать и подчищать хвосты.

Вы понимаете разницу между инкапсулированной инициализацией компонента изнутри, по коллбеку, который вызывается браузером автоматически, и поиском DOM-элементов в документе и инициализацией компонента вручную извне? И тоже самое касается очистки памяти.

можно завернуть ВЕСЬ виджет в один CustomElement и максимально избежать воздействия внешней среды минимально воздействуя на неё в свою очередь.

Виджеты бывают, условно, распределенными по странице: в одном месте может быть, к примеру, UI для загрузки файла, а в другой - прогресс этой загрузки. Помимо этого, у вас может быть библиотека общих UI-примитивов, которая может использоваться как "внутри" так и "снаружи". Помимо этого, вас кейс подразумевает ваш полный контроль над содержимым виджета, а значит, отсутствие проблем с общей областью для имен кастомных тегов. То, что вы пишите, более актуально для Shadow DOM а не CustomElements, надеюсь, вы эти вещи не путаете.

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

Вы все равно пишите атрибуты ref в своей разметке. И никаких проблем с дебагером при использовании биндингов нет, это такой-же javascript, как и все остальное. Более того, вы можете контролировать и обрабатывать дополнительные пограничные случаи на уровне общего решения (что есть в Симбиоте), а в вашем случае - опять пишем ручками повторяющийся код.

Опять-же, предпочитаю не привязывать данные к текстовым нодам а использовать <span ref="key">, ну и собрать строку любым способом мне тоже никто не запрещает.

Вы можете предпочитать такой подход ровно до того момента, пока он не перестанет работать. А он перестанет: есть куча кейсов когда лишние DOM-элементы в разметке начинают ОЧЕНЬ мешать (flex-контейнеры и многое многое другое).

А я ушёл таки в бэк, ибо там где я сейчас работаю, на фронте безраздельно свирепствует React :)

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

Но в данном случае, мы тут сравниваем production-ready библиотеку, которая активно тестируется и используется другими разработчиками в ЗНАЧИТЕЛЬНОМ количестве очень разных кейсов, и ваш эксперимент. Интересный эксперимент, но не более того пока, уж простите.

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

:) "синтаксис кастомных тегов" никогда не проверяется в общем реестре, просто потому, что никогда не попадает в общий скоуп, ну кроме тех случаев, когда это оправданно и делается преднамеренно.

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

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

Ну какие современные стандарты template в продакшене, в 2016 году, ну в самом деле? (Библиотека, способна работать в ИЕ-8 без полифиллов :) )

Вы понимаете разницу между инкапсулированной инициализацией компонента изнутри, по коллбеку, который вызывается браузером автоматически, и поиском DOM-элементов в документе и инициализацией компонента вручную извне? И тоже самое касается очистки памяти.

Компонент - это и есть капсула (DOM + initScript + life cycle handlers), да надо вызвывать component.mount(target, index?) вместо target.appendChild(component) - не большая плата.

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

Это ВСЁ поддерживается, а про общую видимость имён я уже написал чуть выше :)

И никаких проблем с дебагером при использовании биндингов нет, это такой-же javascript,

Предполагаю - он всё-же немного под капотом, про пограничные случаи недопонял,

Вы можете предпочитать такой подход ровно до того момента, пока он не перестанет работать.

Это справедливо для любого подхода

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

Здесь поддерживаю абсолютно, но на изменение ситуации не надеюсь

Но в данном случае, мы тут сравниваем production-ready библиотеку, которая активно тестируется и используется другими разработчиками в ЗНАЧИТЕЛЬНОМ количестве очень разных кейсов, и ваш эксперимент. Интересный эксперимент, но не более того пока, уж простите.

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

Даже не представляю себе кейсов, с которыми связка HTML+JS не справится.

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

Sign up to leave a comment.

Articles