Pull to refresh

Comments 150

Бенчмарки, конечно, странные. Меня привлекло что какая-то (не важно какая) библиотека может в принципе быть быстрее платформы. Я решил немного исправить бенчмарк: https://github.com/eigenmethod/mol/pull/62
Прирост получился ~20x в Firefox Nightly и ~10x в Chromium Nightly.


Теперь в Chromuim даже повторный рендеринг $mol вдвое медленнее обычного нативного рендеринга:)

Я с другими инструментами не знаком:) Был бы Polymer — его бы тоже оптимизировал, но с прочими предоставленными не работал, так что извините.


Как по мне — так производительность фреймворка это на самом деле количество оверхеда, который он привносит. То есть он не может быть быстрее платформы. Понятно, что в более сложных ситуациях вам может быть не очень удобно всё писать без фреймворка, но это не отменяет того факта, что ни $mol, ни React не могут обогнать платформу, которую они используют под капотом.

Добавьте Полимер, сравним :-)


Вы тут сделали 2 оптимизации:


  1. Заменили установку обработчиков событий на делегирование. Это не сильно влияет на результат. Да и оптимизацию эту можно применить к любому фреймворку. А SAPUI5 использует её по умолчанию. Как видно, ему это слабо помогает.


  2. Убрали setTimeout. Из-за чего, стало замеряться лишь время генерации html и установки свойства innerHTML. Без формирования DOM, без раскладки элементов и без собственно их отображения.

image


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

Да, если вы действительно хотите отрендерить, тогда вместо setTimeout() лучше container.offsetHeight, это синхронно вызовет нужные эффекты. Но если на чистоту, то и $mol не рендерит весь список:) Похожий механизм ленивого рендеринга есть и в Polymer: https://elements.polymer-project.org/elements/iron-list


Писать бенчмарк для Polymer ленюсь)

Лучше всё же setTimeout. offsetHeight доведёт дело лишь до layout, но не до painting.


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


Неужели на полимере так сложно реализовать столь простое приложение? ;-)

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


Не сложно, а именно лень.

В реальности, наиболее важным показателем является время между изменением данных и тем моментом, когда пользователь уже может взаимодействовать с интерфейсом. Идеально, если всё это время (включая отрисовку) укладывается в 16мс, что даёт 60 кадров в секунду без дропов.

UFO just landed and posted this here
>Как по мне — так производительность фреймворка это на самом деле количество оверхеда, который он привносит. То есть он не может быть быстрее платформы.

Хорошо что хоть кто-то это понимает :) Являясь мэйнтэйнером пары бэнмарков меня просто поражает различная глупость на которую люди идут ради того чтобы выиграть бэнчмарки, а потом демонстрировать всем какие они молодцы. С некоторыми «оптимизациями» вроде «ленивого» рендера успешно боролся в бэнчмарках, но всё же приходится закрывать глаза на некоторые «оптимизации» из-за которых библиотеки начинают демонстрировать на различных кэйсах чудо производительность.

Весь этот бэнчмаркетинг уи либ — это полнейший трэш.

На самом деле нужно просто обозначить, а что мы, собственно, меряем. Ибо в зависимости от выбранных метрик получатся совершенно разные результаты. Ещё сложнее то, что нужно учитывать взаимодействие с пользователем, к примеру, рендер первого скрина за 100 мс и рендер интерактивной страницы за ещё 1500 мс может быть предпочтительно полному рендеру интерактивной страницы за 1000 мс, если она всё это время белая без содержимого.


Короче, неблагодарное дело вот это всё без четких критериев:)

И зачем вы боретесь с "ленивым рендером"? Конечному пользователю совершенно не важно, рендерится ли что-то в области, которую он не видит, или нет.


Может расскажете поподробней про упомянутый "треш" и "глупые оптимизации", чтобы я мог это всё учесть?

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

>Может расскажете поподробней про упомянутый «треш» и «глупые оптимизации», чтобы я мог это всё учесть?

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

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


Не распишете вкратце как работает этот "умный ресайклинг"? И что за бенчмарк у вас?

>Куда важнее какую производительность показывает именно идеоморфный код, который вы скорее всего напишите с использованием заданного фреймворка.

Я тоже так считал, когда несколько лет назад преследовал цель сделать самую быструю реализацию виртуального дома+компонент, добавил кучу различных оптимизаций в ущерб примитивного API. Но правда в том что в 95% случаев мне совершенно не нужно выжимать каждую микросекунду, пытаться сделать так что на апдэйте будет происходить как можно меньше лишних операций, теперь я понимаю что хочу писать невероятно тупой код, и в реальноых приложениях ничего не будет тормозить, если я не буду делать совсем глупых вещей. А ленивый рендер обычно уместен только в паре компонент вроде дататэйблов, и его не так уж сложно добавить.

Я так же начинал с умной pull-based реактивности с каждым обсёрвбл значением, для списков чуть посложнее специальные эвенты чтобы небыло тысяч листенеров на кэйсах с фильтраций, ну чтобы прям совсем никаких лишних операций небыло :) И может я конечно недостаточно хороший разработчик, но вобщем писать реальное приложение в таком стиле для меня достаточно сложно, особенно отлаживать потом какие-то странные баги в моём коде.

>Не распишете вкратце как работает этот «умный ресайклинг»? И что за бенчмарк у вас?

Когда отрабатывает trackBy алгоритм при обновлении чилдрен листов, каждую ноду можно как-то уникально идентифицировать, и вот когда происходит удаление компоненты, вместо примитивного списка, используется map в который по уникальному идентификатору помещается удалённая компонента. А когда потом с таким же идентификатором она вставляется обратно, то из пула вынимается нужная компонента, в которой ничего не нужно обновлять, просто сделать `insertBefore`. Такой кэйс возможен только в коряво сделаном бэнчмарке, вроде моего :)

https://localvoid.github.io/uibench/

В случае $mol, быстрый показ экрана — это скорее побочный эффект от ленивой архитектуры, чем первоцель. Печально, что всё обсуждение свелось к калибровке линеек.


Взять, к примеру, приложение из начала статьи: https://eigenmethod.github.io/mol/app/supplies/
Число закупок и число позиций в каждой закупке может быть как большим так и маленьким., но (благодаря ленивости) показываться экран будет одинаково быстро. При этом разработчику не придётся судорожно "не так уж сложно добавлять" ленивый рендеринг, когда вдруг выяснится, что у 1% клиентов число позиций в заказе необычайно высоко.


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


Фактически в $mol неотключаемый "trackBy". Любой компонент имеет уникальный идентификатор. И это на самом деле классно, ибо нет ничего плохого в том, что для переноса элемента из начала в конец достаточно просто перенести узел, а не перерендеривать весь список.


Так что бенчмарк в этом смысле как раз таки не коряв. Добавил в него $mol и особых звёзд он не хватает. Несколько напрягает в нём то, что он требует простановки классов. Для $mol в них нет необходимости — он и так проставляет кучу атрибутов для стилизации.

>Фактически в $mol неотключаемый «trackBy». Любой компонент имеет уникальный идентификатор.

Еслиб работал «trackBy», то бэнчмарк бы не показывал красный флажок в «Preserve State», тк не происходит перестановка дом элементов и внутреннее состояние теряется.

>Несколько напрягает в нём то, что он требует простановки классов.

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

Ну так там реализация, которая "трекает" по индексу, а не по идентификатору из исходных данных. Вот и получается, что компоненты стоят на месте и меняют данные внутри.


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

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

Я обычно всё время вырубаю sCU оптимизацию когда бэнчмаркаю всех, и отношусь к этому бэнчу как будто это приходят данные с сервера, поэтому у всех жаваскрипт объектов не сохраняется identity, и лишь только есть параметр «id» по которому можно как-то связывать данные, и если библиотека не умеет в таком случае сохранять внутреннее состояние дом нодов, то моё отношение к такой библиотеке сразу сильно портится :) Во всех популярных УИ либах с этим нет никаких проблем.

>Как минимум можно убрать тесты на простановку классов.

Можно добавить опцию `disableChecks` и не будет никаких проверок :)

https://eigenmethod.github.io/mol/perf/uibench/?disableChecks=true

Я имел ввиду, что реализация на $mol там в лоб — компоненты создаются по индексу. Иначе бы пришлось заводить дополнительный словарь для маппинга идентификаторов на данные. Ведь "сервер" у вас возвращает данные в виде таблицы, а не словаря, как было бы правильнее.


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

>Я имел ввиду, что реализация на $mol там в лоб — компоненты создаются по индексу.

А сорри, не понял сразу :)

>Проверок-то может и не будет, да вот сам бенчмарк завязан на имена классов и падает. Плюс стили к ним привязаны.

Точно, там же всякая фигня с проверкой на scu итп, где вынимаются по класснэймам.

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

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

Не, я её зря добавлял что-ли? Пусть будет. :-)

«Где-то ленивый рендеринг достаётся почти бесплатно и грех им не пользоваться, а где-то надо шаманить с бубном и всё-равно выходит криво, из-за чего его применяют лишь в крайних случаях»
Ну тогда надо сравнивать хотя бы ленивую реализацию с кривой ленивой реализацией другого инструмента.
Я вот у себя открыл первый тест и там для $mol, лениво рендерятся элементы, а для angularjs целиком, хотя там можно поставить angular-vs-repeat который покажет совершенно другие цифры.

У меня в хроме из-за чего-то $mol рендерит все элементы и результаты не такие как у автора ($mol медленнее ангуляра) — скриншот
А в firefox он рендерит всего 60 элементов, а вот ангуляр все 2 тысячи, хотя плагином выше подобный результат элементарно сделать.

Сравнивать надо всё же идеоморфный код, а не специально оптимизированный под конкретный бенчмарк. Поэтому я рекомендую смотреть в сторону ToDoMVC Benchmark.


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


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

«Сравнивать надо всё же идеоморфный код, а не специально оптимизированный под конкретный бенчмарк.»
Мне кажется что вы немного неверно видите суть бенчмарков фреймворков, для них важны скорость решения задачи, безопасность, удобство поддержки и.т.п., а не оптимизированность какой-то одной фичи, которая очень редко используется (я вот angular-vs-repeat всего несколько раз использовал, хотя у меня есть проекты которые я уже по не скольку лет поддерживаю), да и вообще вывод нескольких тысяч элементов одного типа на страницу обычно говорит о проблеме с дизайном и юзабилити сайта, это не совсем программистская задача.

«angular-vs-repeat годится лишь для крайне ограниченного числа случаев, когда у нас список однотипных элементов фиксированной высоты.»

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

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

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

Я думаю, вы могли бы написать интересную статью на эту тему: кто как меряет, какими костылями подпирает и чей велосипед быстрей
Извините, но пихать в id dom-элемента строковое представление вызова функции, еще и не уникальное — это жесть. Именование_модулей_тоже_вызывает_сомнения, читаемость кода очень плохая.

Вообще-то уникальное.
ВыСчитаетеТакоеИменованиеСильноЛучше?

Ну именование всё же дело вкуса, но почему id?


Помню, JSF страдало такой болезнью: если не задать id, JSF сгенерит свой, а если задать — то добавит перед ним id родительских компонентов через двоеточие. Надо ли говорить, что это только запутывает, когда пытаешься работать с jsf-компонентами извне. А получить реальное значение id в DOM совсем нетривиально.


Вот если мне нужно компоненту в $mol задать определённый id, как это сделать?

Как минимум для:


<label for="name">Name</label>
...
<input type="text" id="name" name="name>

Другой пример — якоря


<h1 id="foo">Foo</foo>
<a href="#foo">go to foo</a>

Жёсткий хардкод идентификаторов плохо уживается со сборкой приложений из компонент. Для подписей к полям, я бы рекомендовал использовать $mol_labeler.


Якоря плохо уживаются с SPA приложениями, на которые ориентирован $mol. В общем случае, вы не можете гарантировать, что к моменту перехода по якорю, у вас уже отрендерен элемент с нужным идентификатором (банально, ждём загрузки данных). Поэтому интерактив со скроллингом к нужным частям страницы, реализуется программно. $mol пока не предоставляет обобщённого решения для подобных задач. Но вручную задаваемые идентификаторы тут слабо помогут.


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

В вашем примере у текстового поля тега label вообще нет.


А это важно как для семантики, так и просто для удобства: при клике на label фокус станет в поле ввода автоматически, пользователям проще целиться.

Отлично, вы обнаружили проблему в компоненте. Что нужно сделать?


  1. Создать issue с предложением сделать, чтобы этот компонент рендерился в элемент label.
  2. Не ждать, пока это сделает кто-то, а сделать это самому и предложить pull-request.
  3. Создать свой собственный компонент, как наследника от стандартного, но редрерящегося в label.
  4. Написать в комментариях, что фреймворк ни на что не годен, так как там есть столь фундаментальный архитектурный просчёт.

Семантика для SPA не особо важна, хотя и не лишняя. А вот доступность — да, желательна. Правда я не видел ещё ни одного человека, который бы фокусировал поле ввода кликом по подписи к нему. Единственное полезное применение label — группировка стандартного чекбокса и текста к нему. И то, это скорее костыль. По логике, чекбокс — это частный случай кнопки, которая переключает состояние между несколькими. Код типа такого:


<label> <input type="checkbox"/> Subscribe to subscription </label>

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


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

Отлично, вы обнаружили проблему в компоненте. Что нужно сделать?

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


Для создания issue на Github неплохо сначала начать пользоваться фреймворком, чего я делать не собираюсь.


Семантика для SPA не особо важна, хотя и не лишняя.

Второй ориентирован на веб-страницы, а не веб-приложения.

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

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


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

Лучше была бы аналогия:
"Я не собираюсь пользоваться спорт-каром у которого нет руля и я не могу им управлять"

Скорее уж "я не собираюсь пользоваться спорт-каром, у которого есть ABS". Задача фреймворка — не удовлетворение любых хотелок, а обепечение определённых характеристик. Цели $mol описаны в конце статьи.

Отлично, вам указали на проблему. Что надо сделать?


1) Сказать человеку, чтобы составлял на гитхабе issue
2) Сказать человеку, чтобы делал пул-реквест
3) Сказать, чтобы сам делал компонент, потому что
4) Попытаться исправить ошибку


У вас практически в каждом компоненте присутствуют косяки, я бы устал составлять issues.
Но даже если я их и составлю, я знаю какая будет реакция: не нужно, у меня лучше. Я привёл примеры использования id, что вы ответили? "Не нужно, это контрпримеры. А даже если и нужно, то т.к. мой фреймворк не умеет, то тоже не нужно." Поэтому вы называете свой фреймворк "идеальным"?


Вы постоянно жалуетесь на кривые web-стандарты — формы не те, label не тот, id не нужен. Так пишите письма в w3c со своей аргументированной позицией, пусть сделают как надо.


Касательно того же чекбокса, почему вы сами не проставили нужные атрибуты у элементов? Ок, пускай у вас чекбокс — это button. Ну так и добавьте в неё type="button" role="checkbox". И используйте стандартный атрибут checked. Или вы без issue на гитхабе не сможете это?

Один человек считает это критической ошибкой. Другой — минорной особенностью. Как им друг с другом договориться?


  1. Создать отдельную issue посвящённую этому компоненту и обсудить там все аспекты его поведения. В результате получить хороший компонент, который можно будет использовать в различных случаях.
  2. Кричать, что фреймворк ни на что не годен под статьёй с описанием его общих принципов, аргументируя тем, что "в каждом компоненте есть косяки".

Есть косяки — излагайте. Желательно в виде issue. Комментарии на Хабре — так себе таск-трекер. И пока не напишешь — не узнаешь "не нужно, есть решение лучше" или "спасибо за идею, уже внедряем". Но это ведь не интересно, правда? Куда интересней устроить срач, приписывая оппоненту слова, которые он не говорил.


У вас очень наивное представление о W3C :-) Веб не изменится в одночасье, а приложения нужно делать ещё вчера, на технологиях, выпущенных несколько лет назад.


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

>> Правда я не видел ещё ни одного человека, который бы фокусировал поле ввода кликом по подписи к нему

Я тут. И я считаю, что для людей которые не ставят label к элементам формы есть отдельный котёл в аду. Особенно когда речь заходит о чекбоксах. Особенно с мобильных устройств.

>> Второй ориентирован на веб-страницы, а не веб-приложения.

В чём по вашему принципиальная разница?

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

То есть если страница загружает в процессе работы данные то якоря ей совершенно бесполезны? Гитхабу это расскажите с заголовками в md.

UPD: и хабру тоже (якорь в комментах)

В обоих случаях происходит программная (через JS) прокрутка. Например, на Хабре нет элемента с идентификатором "first_unread".

На хабре — нет, на гитхабе оно тоже не совсем полностью js. Там судя по вёрстке js только «user-content-» префикс добавляет.

Зато вполне есть «comment_9889944» (ваш комментарий) и оно действительно работает как якорь без всякого js

И? Факт в том, что стандартного поведения недостаточно даже для этих недоприложений :-)

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

А по вашему это не так и они сначала грузят веб-клиент, который уже загружает данные?

Гитхаб вроде вполне себе SPA, если вы это подразумеваете.

Да нет, не SPA. Там при переходах грузится новая страница, которая и заменяет старую. Бестолковейшее применение AJAX.

> Бестолковейшее применение AJAX.
А как же избавление от нужды грузить core-компоненты системы? Я знаю, конечно, один способ, версионировать загруженную платформу и в куки записывать версию, тогда сервер может отдавать лишь нужную часть, но ajax попроще будет.

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

Если не делать бандлы, то количество сетевых запросов возрастает резко, если делать, то обновление возможно только целиком. Да и сеть не весь вопрос, загрузка ядра на клиента тоже может быть затратным мероприятием.
А для приложения с миллионами придирчивых пользователей уж точно.
P.S. Я замечаю изменения, и вообще, именно с этого и начались клиентские изменения в сайтах, они не меняют experience, зато экономят ресурсы и компании и клиента.
В этом я не уверен. И даже если это так, то там явно есть «компонент, который загружает данные»

Выглядит он примерно так:


$( document ).on( 'a[href]' , 'click' , event => $.get( this.href + '?_pjax' ).then( html => document.body.innerHTML = html ) )

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

Сильно в этом сомневаюсь, особенно учитывая скорость работы.

Ну зачем мне вас обманывать? Мы же не в детском саду :-)


image

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

Например, недавно генерил idы для элементов списка сгруппированного по указанному (в рантайме) критерию. Реализация была на ангуларе через вложенные ng-repeat'ы, соответственно при клике по кнопке на элементе списка нужно было вызвать функцию, которая бы его (элемент) для начала однозначно идентифицировала через передаваемый ей из разметки (ангулар жеж!) параметр. При том, хотелось элементы идентифицировать «быстро-вычислимо», типа как по индексу в массиве, а не путём линейного поиска по itemId в двумерном js-масииве. В итоге по скорострельности победила реализация с генерацией айдишника по правилу _{{ ext$index }}_{{ int$index}}, с передачей оного в качестве параметра. Как-то так.

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

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

Для подписей к полям, я бы рекомендовал использовать $mol_labeler.

Здорово, только мне нужна не просто подпись, а именно label. Надеюсь, разницу вы знаете? Ведь многие программы для людей с ограниченными возможностями опираются на эту связь (label — input), да и при клике на label связанное поле фокусируется (средствами браузера, а не js), что удобно. Особенно для всяких чекбоксов/радиокнопок.


К слову о чекбоксах: зачем было их делать кнопками? Ведь для этого есть нативный input[type=checkbox]. В вашей чудо-реализации из-за того что используется кнопка (а не input+label), нельзя, например, выделить текст чекбокса. Плюс, эта самая кнопка идёт с дефолтным типом, а, значит, вас ожидает сюрприз, если такой чекбокс окажется внутри тэга form.


Кстати, в примерах я нигде не нашёл, чтобы вы при создании форм использовали этот одноимённый стандартный тэг. Может поэтому вы и не знаете об этих проблемах? И поэтому ваши "формы" не субмитятся по нажатию enter, как это делают обычные формы.


И сдаётся мне, это только вершина айсберга...

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


Касательно кнопок и форм: формы в html тоже имеют один неприятный просчёт в дизайне — они не могут быть вложенными. Из-за этого приходится городить костыли при реализации. При этом всё, что они дают — некоторое достаточно тривиальное поведение к которому всё-равно потом нужно прикручивать сбоку свою логику. В случае SPA компонент для создания форм, конечно же нужен, и текущая реализация мне и самому не очень нравится, но он точно не должен наследовать ограничения html-form.

Почему вы хотите выделять текст у чекбокса, но не хотите выделять текст у кнопок?

Хочу, но не могу. А почему вы решили, что не хочу? С нормальными чекбоксами такой проблемы нет, но у вас она появилась. Так что вы не только наследуете ограничения, но и распространяете их туда, где их не было. Восхитительно!

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


Однако, для пользователя выделение текста на кнопках (и чекбоксах в том числе) — это баг. Он пытается кликнуть, но рука соскочила и кнопка зачем-то выделилась.


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

Не надо думать за меня.


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


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


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

Вы считаете нормальным, чтобы при выделении текста чекбокс переключался?


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

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

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

От ССЗБ никакой фреймворк не спасёт :-) $mol никак не привязывается к идентификаторам элементов. Они вставляются в DOM лишь как "хлебные крошки", помогающие исследовать рантайм.

Поддерживаю. Если уж и хочется что-то запихнуть в атрибуты, то для этого придумали data-* либо свои собственные можно придумать, например, mol-*

Ну так свои и придуманы: mol_*


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

IDE уже умеют в умный рефакторинг, кое-где и с переводом из camelCase в snake_case

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

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


Сами сравните: ГорбатоПлотноеИмя или простое_для_чтения_обозначение.

Можно ли использовать $mol без nodejs? Я, например, использую ruby.

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

В общем, без ноды не обойтись (njs в разы тяжеловеснее того-же ruby. Да и на heroku такое сложно залить)

А на ruby/python портировано вообще когда-нибудь будет?

На heroku нода замечательно запускается.


Нет, тут же используется typescript и postcss, которые написаны на js. Портировать их — проще застрелиться. :-)

Ну, можно сделать обёртку-враппер вместо портирования. Именно так в ruby работает coffeescript.

Но для исполнения всё-равно нужна будет нода. Я не очень понимаю, что будет делать этот "враппер".

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

UPD: да прибудут со мной минусы.
Согласен с уровнем знания английского.
`Builded` убил.

Где вы тут увидели обилие спецсимволов на строку?


Поможете нам с английским? :-)

Ровно весь формат Tree, ровно каждый ваш идентификатор начинается с "$" это не говоря уже о «глобальном» пространстве имён, которое тоже "$", что в результате заставляет писать нечто такое: `$.$`. Основная проблема в том, что все эти спецсимволы новы для того, кто начинает пользоваться вашим фреймворком. В той же скале почти для каждого символа такого рода есть вполне человеческое имя метода, да и каждый новый фреймворк обычно не привносит более 1 метода названного через спецсимвол на десяток классов + те, что есть уже давно зафиксированы в сообществе. Например, есть `::` для слияния списков (и их разбора, но это отдельная тема), есть `->` для создания кортежей и есть фреймворк akka, который из часто используемого привносит только `!` (отправить сообщение) и `?` («спросить» сообщение). При этом для кортежей можно использовать `Tuple2(a, b)`, для отправки сообщения `tell(message)`, для «вопроса» — `ask`, собственно для списков тоже есть метод (вот только я уже забыл его название). В вашем же случае вы просто вбрасываете охапку этих спецсимволов без каких-либо альтернатив и надеетесь, что кому-то будет легко это запомнить.

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

Во view.tree используется всего 8 спецсимволов. Код без спецсимволов воспринимается сложнее (я пробовал). Ограниченный набор наглядных спецсимволов позволяет быстрее ухватывать суть. Возможность писать и так и сяк привносит лишь бардак и не даёт никаких преимуществ.


Нечто такое $.$ писать не нужно. $ по умолчанию эквивалентен глобальному пространству имён. Его пришлось добавить исключительно для совместимости с NodeJS. Вы посмотрите в коде, нигде $.$ не используется. Сами '$' — совсем не новы. Код на некогда популярном jQuery ими утыкан чуть менее, чем полностью.


Впрочем, если отставить в сторону эти "фу, мне не нравится, я так не привык", то как бы вы реализовали view.tree? Вот возьмём, например, следующий код:


$mol_clicker $mol_viewer
    tagName \button
    enabled true
    event * click > eventClick null
    attr *
        disabled < disabled false
        tabindex \0

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


Это соглашение об именовании такое — заканчивать презентеры на "er", что даёт похожие на англоязычные имена.

«Всего 8» — это не «всего». Запомните магическое число 7 — это максимум однотипной не очень понятной ерунды которую среднестатистический человек может держать в голове, соответственно идеально — 3-4. Они не наглядны, ибо нигде раньше в таком контексте не были использованы. Возможность писать «и так и сяк» должна быть по крайней мере до тех пор пока сообщество (а не вы, что важно) не примет один из стандартов.

>> Нечто такое `$.$` писать не нужно.

Оно в статье есть? — Есть. Значит-таки иногда оказывается нужно.

>> Код на некогда популярном jQuery ими утыкан чуть менее, чем полностью.

И вы хотите сказать, что это хорошо?

>> Впрочем, если отставить в сторону эти «фу, мне не нравится, я так не привык», то как бы вы реализовали view.tree?

Да хоть бы и так (https://gist.github.com/anonymous/d05bf00e33fa6e23141178e0aa524cea):
```scala
class MyView[ElementType](val e: TypedTag[ElementType], val clickHandler: (Event) => Any)
extends View
with MySuperBehavior {

val child = div()

override lazy val getTemplate = {
e(
enabled := true,
onclick := clickHandler,
onmouseover :+= { println(s«Someone moved mouse over ${e.tagName}») }
// In reality would be some sort of template method, so there won't be any `getTemplate` methods
getMySuperModifiers()
)(
p(«HEADER PART»),
child,
p(«FOOTER PART»)
)
}

override def renderChild(view: View): Unit = {
child.children.foreach(grand => child.removeChild(grand))
view.getTemplate.applyTo(child)
}
}
```
Пример, очевидно, не рабочий. Если хочется посмотреть на подобную штуку поподробнее — google://udash+scala.js

>> Это соглашение об именовании такое — заканчивать презентеры на «er», что даёт похожие на англоязычные имена.

Эти имена не правильны, большинства этих английских слов либо нет, либо они значат не то, что вы ожидаете. Лучше бы уж тогда по Java косили с повсеместным "-able" оно хотя бы переводится более-менее адекватно, да и по смыслу значительно больше подходит. И ещё раз для закрепления: clicker — «тот, кто кликает», clickable — «то, что по чему можно кликнуть».

Знаю я эту байку про волшебное число 7, но она тут совершенно не к месту.


Сообщество никогда не придёт к консенсусу. Слишком оно разнородно. Поэтому даже плохой стандарт лучше, его полного отсутствия. Тем не менее, обозначения я постарался подобрать такие, чтобы вызывать правильные ассоциации: \ ассоциируется с сырыми данными (экранирование), / ассоциируется со вложенной коллекцией (файловые системы), # ассоциируется с идентификатором, * со множественным выбором и так далее. Число этих обозначений намеренно минимизировано и каждое вводилось лишь при крайней необходимости, после долгого обдумывания. Если сообщество предложит обозначения по лучше, то я с радостью изменю генератор для его поддержки.


Если бы вы внимательно читали статью, а не бегло выискивали к чему бы прицепиться, то заметили бы, что через $.$ было только одно обращение, да и то было в контексте "вы выполнили в консоли такой-то код — сработало". Для совместимости с NodeJS весь код и так пишется внутри неймспейса $, так что его указывать не нужно. По умолчанию $ эквивалентен глобальному неймспейсу, так что через консоль опять же его можно не указывать. Единственное исключение — если вы зачем-то подключили какую-нибудь библиотеку типа jQuery, которая объявляет глобальную переменную $ со всеми вытекающими.


И вы хотите сказать, что это хорошо?

Нет, я говорю, что в этом нет ничего необычного в мире JS.


Далее вы приводите код, который делает совершенно не то, что предложенный мной. Имеет в 3 раза больший объём. Гораздо меньшую гибкость. 12 спецсимволов (против 5 в моём примере). Несколько десятков разных синтаксических конструкций (против 7 в моём примере). Вы действительно считаете эту портянку предпочтительней?


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


namespace $ { export class $mol_clicker extends $mol_viewer {

    /// tagName \button
    tagName() {
        return "button"
    }

    /// enabled true
    enabled() {
        return true
    }

    /// eventClick null
    @ $mol_mem()
    eventClick( next? : any , prev? : any ) {
        return ( next !== void 0 ) ? next : <any> null
    }

    /// event * click > eventClick
    event() {
        return $mol_merge_dict( super.event() , {
            "click" : ( next? : any , prev? : any )=> <any> this.eventClick( next , prev ) ,
        } )
    }

    /// disabled false
    disabled() {
        return false
    }

    /// attr * 
    ///     disabled < disabled 
    ///     tabindex \0
    attr() {
        return $mol_merge_dict( super.attr() , {
            "disabled" : ()=> <any> this.disabled() ,
            "tabindex" : ()=> <any> "0" ,
        } )
    }

} }

"clicker" в данном случае — сокращение от "clickable presenter". Можно было бы ввести суффикс типа "_view" или "View", но тогда получаются совсем стрёмные имена вида "$mol_grid_view_row_view" и "$mol_gridView_rowView". Совсем без суффиксов — получается путаница между фабриками визуальных компонент и данных для них. Например, свойство "row" может возвращать данные для строчки таблицы, а "rower" — компонент, который эту строчку визуализирует.

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

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

Как вы думаете почему я первую половину статьи прочитал, а остальную бегло просмотрел и начал «выискивать»? Всегда задавайте себе такой вопрос, когда кто-либо относительно аргументировано начинает критиковать — обычно это значит, что вы что-то не учли или не просчитали либо при написании/проектировании, либо при выборе target группы. Так вот, начал я к "$" докапываться потому что у меня возникло ощущение, что я читаю статью о php/bash/perl, а не о typescript. А про спецсимволы я свою позицию уже объяснил.

Вам это не говорит о том, что код не понятен? Кхм, мой пример имеет в разы большую гибкость, вплоть до выбора типа элемента + оба возможных метода расширения функционала: наследование и композиция элементов. Уж не знаю как вы считали (генерики с лямбдами учли небось?), но для скала разработчика там всего 2 спец символа и те вполне понятны, особенно если уточнить, что ":" просто суффикс, ибо "=" не перегрузить, а именованых вараргов в отличие от питона не завезли. Касательно конструкций — там лишь наследование, генерики, свойства, методы и лямбды — ничего сверхестественного или непонятного. И да, я считаю эту «портянку» ЗНАЧИТЕЛЬНО более предпочтительной, а для тех, кому трудновато пока освоиться с нестандартным синтаксисом, но очень понравилась идея — рекомендую Binding.scala, что выглядит как реакт, с той лишь разницей, что оно действительно реактивно.

И вот моя попытка номер 2 в осознании вашего кода (https://gist.github.com/anonymous/d56c6665ab6fbea6c162f2f76abbc983):

// literally rewrote
def clickableElement(text: Modifier): TypedTag[Button] =
button(
enabled := true,
onclick := { false },
disabled := false,
tabIndex := 0
)(text)

// More generic way:
def button(text: => Modifier): TypedTag[Button] =
typedTag[Button](«button»)(
enabled := true,
onclick := { false },
disabled := false,
tabIndex := 0
)(text)

// My thoughts about what it was…
class Button extends View {
protected def enabled = true
protected def disabled = false
protected def clickHandler: (Event) => Boolean = { e => false }
protected def tabIndex = 0

abstract protected def text: Modifier

def render = button(
enabled := { this.enabled || !disabled }
onclick := clickHandler
tabIndex := { tabIndex }
)
}

>> «clicker» в данном случае — сокращение от «clickable presenter»…
Вы уж определитесь с тем, что это такое: вью, презентер (кстати рекурсивный каламбур по вашей логике получается :) ) или компонент и с тем, для чего вы ставите эти суффиксы как следствие. Я вам предложил суффикс который бы отделял компоненты от полей. И чем вас так не устроил вариант "$mol.rowInGridView"

Думаю вам стоит несколько расширить свой кругозор.


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


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


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


Но вернёмся в конструктивное русло. $ используется для того, чтобы обозначить глобальное пространство имён. Именно благодаря ему, сборщик быстро находит зависимости. И именно благодаря ему, вы можете быстро найти все упоминания компонента в любом типе файлов (даже в readme.md), с использованием любого текстового редактора. Исключение только одно — css. Хотя, возможно стоит это пофиксить. Правда селекторы тогда получатся не очень привлекательными: [\$mol_clicker]. Но от единообразия больше пользы.


Для $mol разработчика, в коде view.tree тоже нет ничего нового :-) Однако, скалу знает не так уж много людей. А хотят на ней программировать и тем более верстать — ещё меньше. Так что оба языка одинаково бесконечно далеки от мейнстрима на данный момент. Но если view.tree можно изучить за пару часов вдоль и поперёк, то на скалу придётся потратить существенно больше времени. Не знаю, зачем вы привели первых два варианта, которые опять же делают совершенно не то, но вот последний уже ближе к истине.


Подозреваю, вы не дочитали до того момента, где объясняется, почему вместо описания класса на TypeScript был введён специальный язык. Он позволяет описывать класс в иерархической форме, при этом не теряя в гибкости, позволяя перегружать любой аспект его поведения. У вас же, последнем примере, прямая калька с TypeScript класса. Попробуйте реализовать аналог $mol_pager:


$mol_pager $mol_viewer
    childs /
        < header $mol_viewer
            childs < head /
                < titler $mol_viewer
                    childs /
                        < title
        < bodier $mol_scroller
            childs < body /
        < footer $mol_viewer
            childs < foot /

Который разворачивается в:


namespace $ { export class $mol_pager extends $mol_viewer {

    /// titler $mol_viewer childs / < title
    @ $mol_mem()
    titler( next? : any , prev? : any ) {
        return new $mol_viewer().setup( obj => { 
            obj.childs = () => [].concat( this.title() )
        } )
    }

    /// head / < titler
    head() {
        return [].concat( this.titler() )
    }

    /// header $mol_viewer childs < head
    @ $mol_mem()
    header( next? : any , prev? : any ) {
        return new $mol_viewer().setup( obj => { 
            obj.childs = () => this.head()
        } )
    }

    /// body /
    body() {
        return [] as any[]
    }

    /// bodier $mol_scroller childs < body
    @ $mol_mem()
    bodier( next? : any , prev? : any ) {
        return new $mol_scroller().setup( obj => { 
            obj.childs = () => this.body()
        } )
    }

    /// foot /
    foot() {
        return [] as any[]
    }

    /// footer $mol_viewer childs < foot
    @ $mol_mem()
    footer( next? : any , prev? : any ) {
        return new $mol_viewer().setup( obj => { 
            obj.childs = () => this.foot()
        } )
    }

    /// childs / 
    ///     < header 
    ///     < bodier 
    ///     < footer
    childs() {
        return [].concat( this.header() , this.bodier() , this.footer() )
    }

} }

Вариант $mol.rowInGridView плох тем, что его имя вылезает за пределы модуля $mol.gridView.

>> Думаю вам стоит несколько расширить свой кругозор.

Дело не в конкретном числе, а именно в малом количестве — я не просто так подметил, что в реальности это число ближе к 4. В целом это больше говорит о KISS. Т.е. никто не будет на самом деле сидеть и считать «а не привысило ли число абстрактной ерунды в в этой штуковине число 7?». Кстати не очень понимаю зачем в той статье упомянуты дизайнеры — мне всегда казалось, что у них число 3 более важно (максимальное число переходов, максимальное число шрифтов/цветов...)

>> Нет никакой рациональной причины фрагментировать сообщество.
У вас нет выбора «фрагментировать своё сообщество или нет» у вас выбор «будет у меня фрагментированное сообщество или его не будет совсем». Сомневаюсь что найдётся много разработчиков (не студентов, которым время девать некуда и не юниоров, которым надо просто «побольше выучить, чтоб казаться про»), которые бы стали копать вашу документацию и разбираться в тонкостях новых способов разметки, если им нужно решение здесь и сейчас, это даже для нелегаси проектов не прокатит.

>> Это нормально, что один и тот же символ в разных контекстах вызывает разные ассоциации.
Нет, это скорее исключение. "!=" везде «не равно» (haskell не в счёт) и "+" везде плюс. Не надо усложнять и так не простую жизнь разработчика новыми значениями для привычных вещей.

>> Целью статьи было поделиться свежими идеями
Я в самом первом комментарии написал, что идея хороша, а вот реализация подкачала.

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

>> $ используется для того, чтобы обозначить глобальное пространство имён. Именно благодаря ему, сборщик быстро находит зависимости.
Вы сейчас про typescript или про префиксы своих компонентов пишете?

>> Для $mol разработчика, в коде view.tree тоже нет ничего нового
Есть принципиальная разница: для scala-разработчика в udash и Binding.scala нет кардинально нового, а для ts-разработчика (и тем более js) в $mol сликом много нового.

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

>> Подозреваю, вы не дочитали до того момента, где объясняется, почему вместо описания класса на TypeScript был введён специальный язык.
Либо я плохо искал, либо у вас нет этого объяснения. У вас есть только сравнение с xml-подобным синтаксисом. Не самое убедительное кстати.

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

>> Попробуйте реализовать аналог
https://gist.github.com/anonymous/17043c00b00117eb7af266df9cd7b802:

/**
* param header Reactive property binding
* param content Reactive property binding
* param footer Reactive property binding
* param attrs Attributes
*/
case class Page(
private val header: Property[Element] = Property[Element],
private val content: Property[Element] = Property[Element],
private val footer: Property[Element] = Property[Element],
private val attrs: Modifier*
) {
def render = div(attrs: _*)(
header,
content,
footer
)

def apply(
header: Property[Element] = this.header,
content: Property[Element] = this.content,
footer: Property[Element] = this.footer,
modifiers: Modifier* = this.attrs
) = copy(
header = header,
content = content,
footer = footer,
modifiers = modifiers
)
}

val myContent = Property(p(«hello»).render)
val mainPage = Page(style := MainPage.styles)(
header = Property(h2(«Hello»))
content = myContent
)

// application in body

body(
onclick :+= { myContent.set(p(«world»)) }
mainPage.render
)

>> Вариант $mol.rowInGridView плох тем, что его имя вылезает за пределы модуля $mol.gridView.
$mol.grid.row.RowView

Ну так KISS и является основным принципом при разработке $mol и view.tree в частности. Меньше синтаксических конструкций фиг сделаешь. Разве что prop @ \value можно заменить на l10n#prop \value, но тогда будут сложности с перегрузкой отдельных текстов, ведь все тексты будут находиться в одном свойстве, но по разным ключам.


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


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


Введение отдельного языка view.tree — вынужденная мера, чтобы уменьшить сложность, а не самоцель. Без него, пришлось бы либо писать кучу плоского кода на TypeScript, либо отказываться от таких плюшек как "контроль жизненного цикла" и "человекопонятные идентификаторы" и фигачить кучу кода на TSX, либо фигачить кучу ещё менее понятного кода на XML (синтаксис-то хоть из знаком, но необходимость осваивания новых идиом никто не отменял).


В середине главы "С голой грудью на амбразуру" как раз приводится иллюстрация того, как view.tree позволяет объявлять плоский класс, при этом не теряя наглядности иерархии:


$mol_app_supplies_positioner $mol_carder
    heightMinimal 68

    content < grouper $mol_viewer childs /

        < mainGroup $mol_rower childs /

            < productItem $mol_labeler
                title < productLabel @ \Product
                content < productName \

            < costItem $mol_labeler
                title < costlabel @ \Cost
                content < coster $mol_coster
                    value < cost $mol_unit_money
                        valueOf 0
- ...

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


В вашем коде потерялись следующие свойства: head, body, foot, позволяющие изменить содержимое соответствующих блоков, не меняя сами блоки. Кроме того, потерялся вложенный блок titler, который по умолчанию выводится в header, и который выводит внутри себя свойство title, которое есть у всех компонент. Содержимое свойства title корневого элемента, кстати, выводится в заголовке окна автоматически. А так как любой компонент может быть корневым, то это свойство изначально есть у всех компонент. И опять же, у вас получилось много запутанного плоского кода, вместо лаконичного иерархичного декларативного на view.tree.


+ в контексте URL ассоциируется с пробелом, в контексте версий ассоциируется с открытым сверху диапазоном. = в контексте URL ассоциируется с разделителем ключ-значение, в контексте языка программирования с присвоением, в контексте SQL — со сравнением. Так вот #во многих контекстах ассоциируется с идентификатором, поэтому и была выбрана на роль свободного идентификатора. И, кстати, пока мы тут спорили и уже запомнили, что # — это идентификатор в контексте view.tree, так что никаких сложностей в идентификации идиомы этот символ у вас уже не вызовет :-)


Вот вы жалуетесь, что реализация подкачала. Но что вы предлагаете? Перевести весь код на scala? Вы действительно думаете, что это повысит популярность фреймворка в мире, где половина сидит на Angular, половина переходит на React (что бы это ни значило), а большая половина поддерживает легаси на jQuery? Или вы предлагаете скрестить ужа с ежом — собрать химеру из JXS и Angular, чтобы и тем и другим было проще перейти, но по факту получить лишь вопросы в духе "ну и зачем ещё один велосипед, который делает то же, что и реакт?!"?


Касательно сборщика — его логика работы не завязана на конкретные языки. В одном модуле могут быть файлы css, js, ts, view.tree и другие в различных пропорциях. При этом зависимость может подтянуться в любом из них. Например, вы можете использовать в css селектор вида [my_support_grid], который приведёт к загрузке модуля my/support, который проставит в body этот самый атрибут my_support_grid, если детектирует поддержку css-grid в браузере. Ну а если в приложении ни одно правило не завязалось на my_support_grid, то соответствующий модуль загружен не будет.


$mol.gridView и $mol.grid.row.RowView опять же имеют разные пространства имён. Тогда уж $mol.grid.view и $mol.grid.row.view. Но тут получается, что все презентеры будут иметь одно и то же имя класса, но в разных неймспейсах, что не очень удобно в работе — в них легко запутаться.

Про первые четыре абзаца:

Вы JSX видели? Видели чем он по факту является? Так вот я предлагаю вам не городить свои велосипеды и не писать свой парсер (который к слову будет иметь баги, каким бы крутым разработчиком вы не были), а взять уже готовое решение — JSX, mustache, nunjucks или в крайнем случае написать свой DSL (не уверен на счёт TS, но в котлине или скале оно делается совсем не сложно)

>> Вот вы жалуетесь, что реализация подкачала. Но что вы предлагаете? Перевести весь код на scala?
Боже упаси, нет конечно. Не думайте, что я упоротый и пытаюсь воткнуть любимую скалу повсеместно. Это были лишь примеры того, что не обязательно велосипедировать и вполне можно обойтись простеньким DSL-ем.

>> но по факту получить лишь вопросы в духе «ну и зачем ещё один велосипед, который делает то же, что и реакт?!»?
Сомневаюсь, что кто-либо будет задавать такие вопросы. В конце концов вы пилите один из немногих на самом деле реактивных фреймворков + насколько я понял ваши идеи — это сотворить современный extJS «с блекджеком...»

>> В вашем коде потерялись следующие свойства: head, body, foot, позволяющие изменить содержимое соответствующих блоков, не меняя сами блоки
Они через конструктор передаются.

>> Кроме того, потерялся вложенный блок titler, который по умолчанию выводится в header, и который выводит внутри себя свойство title, которое есть у всех компонент.
Представьте себе ситуацию, когда кто-то изменит одновременно header, titler и title и после этого начнёт удивляться что же пошло не так. Нельзя давать слишком много гибкости. Вилкой тоже можно суп съесть, но зачем.

>> Касательно сборщика
Вам не кажется более логичным использовать как разделитель точку? И это всё равно не объясняет зачем вам "$"

>> $mol.gridView и $mol.grid.row.RowView опять же имеют разные пространства имён. Тогда уж $mol.grid.view и $mol.grid.row.view
$mol.grid.GridView и $mol.grid.row.RowView
Вы JSX видели? Видели чем он по факту является?

Код вида


        return <div>
            <div id="header">
                <div id="titler">{ this.title }</div>
            </div>
            <div id="bodier">{ this.body }</div>
            <div id="footer">{ this.foot }</div>
        </div>

транслируется в


        return R.createElement("div", null, 
            R.createElement("div", {id: "header"}, 
                R.createElement("div", {id: "titler"}, this.title)
            ), 
            R.createElement("div", {id: "bodier"}, this.body), 
            R.createElement("div", {id: "footer"}, this.foot));

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


class Pager {
    title() {
        return ''
    }
    head() {
        return [ this.titler ] as any[]
    }
    body() {
        return [] as any[]
    }
    foot() {
        return [] as any[]
    }
    titler() {
        return <div id="titler">{ this.title }</div>
    }
    header() {
        return <div id="header">{ this.head }</div>
    }
    bodier() {
        return <div id="bodier">{ this.body }</div>
    }
    footer() {
        return <div id="footer">{ this.foot }</div>
    }
    childs() {
        return [ this.header , this.bodier , this.footer ] as any[]
    }
    render() {
        return <div>{ this.childs }</div>
    }
}

Но при таком использовании смысла в XML нет ровным счётом никакого. Какая разница писать ли


<div id="bodier">{ this.body }</div>


или же


new Div({ id : 'bodier' , childs : this.body })


? Кода получается примерно столько же, гибкость одинаковая, возможностей второй вариант даёт больше, а костылей и транспиляторов во втором варианте меньше. view.tree же делает это "раздербанивание" автоматически. К TSX это не прикрутить. Но можно прикрутить ко внешнему XML — точно так же разбирать его и генерировать TypeScript класс. Но как я уже говорил, наглядность у XML хуже, а кода больше, плюс костыли нужны для словарей, значениями которых могут быть не только строки.


mustache, nunjucks

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


или в крайнем случае написать свой DSL (не уверен на счёт TS, но в котлине или скале оно делается совсем не сложно)

view.tree — и есть DSL. Он предельно прост, декларативен и эффективно решает все задачи связанные с описанием и композицией компонент. С ним может работать как простой верстальщик, который не умеет в JS/TS/scala, так и программист, который своими хитрыми скриптами никак не задевает верстальщика.


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

Вы чуть выше задаёте эти вопросы. "Зачем view.tree, если есть jsx/mustache/nunjucks/dom-builder, которые делают то же самое". В том-то и дело, что view.tree — не шаблонизатор, а язык для описания и композиции компонент. Сами компоненты могут быть вообще никак не связаны с DOM.


ваши идеи — это сотворить современный extJS «с блекджеком...»

Единственное сходство с ExtJS — и тот и другой предоставляют базовый набор готовых компонент. Это делает любой компонентный фреймворк.


Они через конструктор передаются.

Через конструктор передаются сами блоки header, bodier, footer, а не их содержимое. И каждый раз при использовании нужно вручную создавать каждый из этих блоков. Во view.tree вы можете перегрузить как сам блок (bodier), так и только лишь его содержимое (body). Во втором случае будет сгенерирован дефолтный bodier, в который и будет вставлено содержимое.


Представьте себе ситуацию, когда кто-то изменит одновременно header, titler и title и после этого начнёт удивляться что же пошло не так.

И что же там может пойти не так, например?


Нельзя давать слишком много гибкости.

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


https://github.com/wmira/react-panel/blob/master/src/panel.js
https://github.com/react-bootstrap/react-bootstrap/blob/master/src/Panel.js
https://github.com/Theadd/react-panels/blob/master/src/jsx/panel.js
https://github.com/pivotal-cf/pivotal-ui/blob/development/library/src/pivotal-ui-react/panels/panels.js


Это аналоги десятистрочного $mol_pager на реакте.


Особенно мне нравится этот чудесный пример использования:


<Panel className="bg-neutral-10" header={<h2>Custom Title</h2>} actions={<div><button>Go</button><button>Stop</button></div>}>
 Base Panel with custom header and actions
</Panel>

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

Символ подчёркивания — единственный символ, который можно одинаково использовать хоть в JS, хоть в CSS, хоть ещё где. К тому же, благодаря этому, имя класса всегда содержит полный путь к нему (а не только последнюю его часть), что используется для генерации аттрибутов, идентификаторов и просто удобно при дебаге.


И это всё равно не объясняет зачем вам "$"

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


$mol.grid.GridView и $mol.grid.row.RowView

Уже лучше, но как-то костыльно — для каждого класса отдельный неймспейс. Тогда уж $mol.grid.GridView и $mol.grid.RowView, но какой должен сгенерироваться из них класс/атрибут для стилизации в css?

То, что вы не правильно поняли концепт реакта не говорит о том, что он плохо расширяется. Это говорит лишь о том, что если вы пилите компонент панели, то всё, что она о себе должна знать — это то, что у неё возможно (nullable) есть шапка, тело и футер, а вот что в них будет решат уже они сами (собственно в моём примере на scala-tags+udash это и реализовано).


К TSX это не прикрутить

Вы в этом уверены? Насколько я понял tsx/jsx транслируется примерно в такие вызовы (псевдокод): tagName[T](children: List[T], attributes: Map[String, Object]): T


Но как я уже говорил, наглядность у XML хуже, а кода больше

Это лично ваше мнение.


view.tree — и есть DSL

view-tree — это DSL на базе самописного формата. Я же имел ввиду именно DSL на базе стандартных возможностей языка, хоть в JS это и не популярно.


Во view.tree вы можете перегрузить как сам блок (bodier), так и только лишь его содержимое (body). Во втором случае будет сгенерирован дефолтный bodier, в который и будет вставлено содержимое.

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


Это вообще шаблонизаторы

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


Единственное сходство с ExtJS — и тот и другой предоставляют базовый набор готовых компонент. Это делает любой компонентный фреймворк.

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


И что же там может пойти не так, например?

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


Иначе у вас неизбежно будет получаться вот такой вот говнокод

Первый это чьё-то поделие (не в обиду автору), количество звёзд говорит само за себя.
Второй — обёртка над бутстрапом никакой жести там кроме самого бутстрапа (способов его конфигурирования) нет.
Третий — пример идеального проектирования (ИМХО) и полного понимания что такое композиция элементов и как её нужно использовать.
Четвёртый — пример того как не надо воспринимать композицию элементов.
И ни один из них не говорит о "неизбежности говнокода", как и о недостаточной гибкости реакта.


Это аналоги десятистрочного $mol_pager на реакте.
Разве что первый.

Символ подчёркивания — единственный символ, который можно одинаково использовать хоть в JS, хоть в CSS, хоть ещё где

"ё" тоже можно одинаково везде использовать, но-таки не стоит. Лучше ввести несложные правила замены, тогда семантически везде будет одно и то же.


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

В ts же вроде как аннотации есть, зачем тогда так извращаться?


Уже лучше, но как-то костыльно — для каждого класса отдельный неймспейс

Ровно до тех пор, пока не появятся другие классы в неймспейсе, например, CustomerDao и CustomerView в mycompany.search.customer


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

Сомнительный момент для, например, mycompany_search_customer_results_card_subdivision_account_identification_place_registration и человека, который разрабатывает форму лицевого счёта. И поверьте, в SID бывает ещё и не такое...


но какой должен сгенерироваться из них класс/атрибут для стилизации в css?

FQCN, до тех пор, пока не задано иное явно.

Это говорит лишь о том, что если вы пилите компонент панели, то всё, что она о себе должна знать — это то, что у неё возможно (nullable) есть шапка, тело и футер, а вот что в них будет решат уже они сами (собственно в моём примере на scala-tags+udash это и реализовано).

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


Вы в этом уверены? Насколько я понял tsx/jsx транслируется примерно в такие вызовы (псевдокод): tagName[T](children: List[T], attributes: Map[String, Object]): T

Я в предыдущем сообщении привёл привёл код, который генерируется. Зачем вы фантазируете?


Это лично ваше мнение.

Я его аргументировал. А у вас есть что возразить, кроме перехода на личности?


view-tree — это DSL на базе самописного формата. Я же имел ввиду именно DSL на базе стандартных возможностей языка, хоть в JS это и не популярно.

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


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

Представьте себе компонент "календарик". На тулбаре у него есть кнопки "предыдущий месяц", "следующий месяц" и "текущая дата". Вам нужен точно такой же, но с перламутровыми пуговицами кнопками "предыдущий месяц", "следующий месяц" и "ближайшая дата отчёта". Через view.tree это реализуется элементарно:


$my_report_calendar $mol_calendar
    tools /
        < monthPrever
        < nearester $mol_clicker
            eventClick > eventNearest null
            childs / < nearesterLabel @ \Nearest
            title < nearesterHint @ \Nearest report date
        < monthNexter

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

Откуда эта странная любовь к костылям к сторонним решениям, вместо написания своего без костылей?


Может внезапно не оказаться вложенного элемента который ожидался и кто-то потратит время на поиск глупейшей ошибки

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


И ни один из них не говорит о "неизбежности говнокода", как и о недостаточной гибкости реакта.

У меня получились несколько иные выводы


"ё" тоже можно одинаково везде использовать, но-таки не стоит.

В идентификаторах её нельзя использовать.


Лучше ввести несложные правила замены, тогда семантически везде будет одно и то же.

А зачем вводить дополнительные правила, если их можно не вводить? Только потому, что вы привыкли в-css-писать-имена-классов-через-дефисы, а.в.javascript.через.Точки?


В ts же вроде как аннотации есть, зачем тогда так извращаться?

Аннотации тут ни чем не помогут. Да и кроме TS есть и другие языки.


Ровно до тех пор, пока не появятся другие классы в неймспейсе, например, CustomerDao и CustomerView в mycompany.search.customer

Тогда незачем тавтология с "customer.Customer": "mycompany.search.customer.Dao", "mycompany.search.customer.Vew".


Сомнительный момент для, например, mycompany_search_customer_results_card_subdivision_account_identification_place_registration и человека, который разрабатывает форму лицевого счёта. И поверьте, в SID бывает ещё и не такое...

И что тут сомнительного? Ну, кроме того, что глубина вложенности скорее всего преувеличена. И что такое SID?


FQCN, до тех пор, пока не задано иное явно.

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

> Представьте себе компонент «календарик». На тулбаре у него есть кнопки «предыдущий месяц», «следующий месяц» и «текущая дата». Вам нужен точно такой же, но с перламутровыми пуговицами кнопками «предыдущий месяц», «следующий месяц» и «ближайшая дата отчёта». Через view.tree это реализуется элементарно:

Это реализуется не элементарно, а за счет усложнения путем введения паттерна «наследование», который всегда хуже, чем композиция. И вот у вас 2 несвязанных компонента, может быть даже в разных проектах становятся связанными.
Однако mol или React тут не причем, в React наследование реализуется точно так же

class A extends React.Component(){
getPanel1(){
return Panel1
}
}
class B extends A{
getPanel1(){

}
}
Это реализуется не элементарно, а за счет усложнения путем введения паттерна «наследование», который всегда хуже, чем композиция.

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


И вот у вас 2 несвязанных компонента, может быть даже в разных проектах становятся связанными.

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


Однако mol или React тут не причем, в React наследование реализуется точно так же

Разница в том, что в реакте нужно специально выносить в отдельные функции, раздувая код и теряя все преимущества JSX, а в $mol оно получается само, потому что иначе нельзя, кода получается меньше, а иерархия не теряет своей наглядности.

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

Крайне странный пример, который не показывает в чём же у меня в этой ситуации возникнут проблемы. Особенно при условии, что футер и шапка поумолчанию null и не выводятся.


Я в предыдущем сообщении привёл привёл код, который генерируется. Зачем вы фантазируете?

Вы привели код, который генерирует реакт+jsx, а не jsx.


Я его аргументировал. А у вас есть что возразить, кроме перехода на личности?

Вы его не аргументировали. "Мне не нравится" и "Слишком многословно" — это не аргументы в пользу "XML не наглядный".


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

Вложенные вызовы, лямбды, ...


Через view.tree это реализуется элементарно

При нормальном проектировании компонента это где угодно элементарно реализуется.


Откуда эта странная любовь к костылям к сторонним решениям, вместо написания своего без костылей?

Вы точно в продакшне работаете? Своё — это, за редким исключением, велосипед на костылях, который требует поддержки и покрытия.


Если перегружается компонент, который его содержит, то странно ожидать, что вложенные компоненты

Тем не менее это будет наверно самая популярная ошибка.


У меня получились несколько иные выводы

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


В идентификаторах её нельзя использовать.

Консоль огнелиса:


>> var ё="можно"
<< undefined
>> ё
<< "можно"

А зачем вводить дополнительные правила, если их можно не вводить? Только потому, что вы привыкли в-css-писать-имена-классов-через-дефисы, а.в.javascript.через.Точки?

Именно! Потому что эти символы в этих языках имеют смысл и не пересекаются с уже установленными соглашениями.


Аннотации тут ни чем не помогут. Да и кроме TS есть и другие языки.

@Component
class MyComponent {}

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


Тогда незачем тавтология

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


И что тут сомнительного? Ну, кроме того, что глубина вложенности скорее всего преувеличена. И что такое SID?

Сомнительно, что человек в здравом уме захочет иметь такие имена классов. А пример — не преувеличение. SID — что-то вроде унифицированной базовой модели любой биллинговой системы.


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

Вам примеры FQCN (Fully Qualified Class Name) нужны?

Крайне странный пример, который не показывает в чём же у меня в этой ситуации возникнут проблемы. Особенно при условии, что футер и шапка поумолчанию null и не выводятся.

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


Вы привели код, который генерирует реакт+jsx, а не jsx.

Я привёл код, который генерируется из TSX. И реакта там нет, если вы не заметили.


Вы его не аргументировали. "Мне не нравится" и "Слишком многословно" — это не аргументы в пользу "XML не наглядный".

Очень жаль, что это всё, что вы заметили.


Стандартными возможностями языка TypeScript нельзя иерархически объявлять свойства — только в плоском виде.
Вложенные вызовы, лямбды, ...

Стандартными возможностями языка TypeScript нельзя иерархически объявлять свойства


При нормальном проектировании компонента это где угодно элементарно реализуется.

Идея $mol в том, чтобы программисту было легче написать "нормальный" код, чем "ненормальный", без какого-то "особого проектирования", которое усложняет, раздумает и замедляет код.


Вы точно в продакшне работаете? Своё — это, за редким исключением, велосипед на костылях, который требует поддержки и покрытия.

Точно. Что ж, вам своё лучше не пилить. Воспользуйтесь моим ;-)


Тем не менее это будет наверно самая популярная ошибка.

Сомневаюсь. Это очень странный и нелогичный кейс.


Которые опять-таки не говорят о ущербности реакта, а разве что о ущербности разработчиков конкретных библиотек

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


у вас там совершенно не честное сравнение вашего компонента, который почти ничего не умеет, и сферических космолётов в вакууме.

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


В идентификаторах её нельзя использовать.
Консоль огнелиса:

Ок, можно. Но смысла всё-равно нет.


Только потому, что вы привыкли в-css-писать-имена-классов-через-дефисы, а.в.javascript.через.Точки?
Именно! Потому что эти символы в этих языках имеют смысл и не пересекаются с уже установленными соглашениями.

Эти соглашения создают определённые проблемы. Можно лепить костыли типа конвертации и поддержки IDE, а можно использовать другие соглашения, которым костыли не требуются. Я за второй вариант.


Аннотации тут ни чем не помогут. Да и кроме TS есть и другие языки.
Component
class MyComponent {}

Я знаю, что такое аннотации, каких типов бывают, как работают и какие имеют ограничения. Они тут ничем не помогут.


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

Что вы там и куда в css собираетесь транспилировать?


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

О том и речь, поэтому и используется суфикс "er".


Сомнительно, что человек в здравом уме захочет иметь такие имена классов. А пример — не преувеличение. SID — что-то вроде унифицированной базовой модели любой биллинговой системы.

Преувеличение. У вас похоже получился слишком специфичный компонент.


Вам примеры FQCN (Fully Qualified Class Name) нужны?

Пример css селектора.

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

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


Я привёл код, который генерируется из TSX. И реакта там нет, если вы не заметили.

Не суть, главное, что у JSX есть интерфейс для интеграции. + где-то на просторах интернета была статья о использовании JSX как я описал, вот только ссылочка потерялась.


Стандартными возможностями языка TypeScript нельзя иерархически объявлять свойства

Проявите фантазию:


interface Attribute {
    name: String
    value: String | Number
}

type Content = Element | String

interface Element {
    attributes: Array<Attribute>
    children: Array<Content>
}

type ElementConstructor = (attrs: Array<Attribute>, children: Array<Content>) => Element
type DSLContent = Attribute | Content

function simpleProp(key: String, value: String | Number): Attribute {
}

function buildElement(construct: ElementConstructor, ...content: DSLContent[]): Element {
    return construct(
        <Attribute[]> content.filter(it => it instanceof Attribute),
        <Content[]> content.filter(it => it instanceof Content)
    );
}

function curry<A, BA, B extends Array<BA>, R>(source: (A, B) => R): (A) => (...BA) => R {
    return a => b => source(a, b)
}

function myComponent(): Element {
}
function myBaseComponent(attrs: Attribute[], content: Content[]): Element {}

var dsl = curry<ElementConstructor, DSLContent, Array<DSLContent>, Element>(buildElement)

dsl(myBaseComponent)(
    simpleProp("id", "myBaseComponent"),

    myComponent(),
    "hello",
    dsl(myBaseComponent)(
        simpleProp("id", "myBaseComponent2")
    ),
    myComponent()
)

P.S. В typescript не очень-то умею.


Идея $mol в том, чтобы программисту было легче написать "нормальный" код, чем "ненормальный", без какого-то "особого проектирования", которое усложняет, раздумает и замедляет код.

Не бывает "особого" или не "особого" проектирования, оно либо есть, либо код загнивает. + у каждой библиотеки есть своя философия и если её не придерживаться то непременно выйдет говнокод.


Точно. Что ж, вам своё лучше не пилить. Воспользуйтесь моим ;-)

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


О том и речь, что мало кто способен на реакте хорошо реализовать даже такой простой компонент

Вы путаете, там реализовывали не простые компоненты, а универсальные вундервафли, так что не в счёт.


Всё честно. Компонент умеет ровно то, что необходимо и ничего лишнего.

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


Ок, можно. Но смысла всё-равно нет.

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


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

И потом упорно объяснять, что вы не верблюд когда у вас спрашивают "чем же вас не устроили общепринятые стандарты?"


Что вы там и куда в css собираетесь транспилировать?

Я про JS


О том и речь, поэтому и используется суфикс "er".

Это-то тут причём? Если все мои классы вдруг начнут называться Classer я наверно приму решение о самовыпиле из мира сего, собственно названий Viewer для всех вьюх тоже будет достаточно.


Преувеличение. У вас похоже получился слишком специфичный компонент.

Для этой области вполне обычное дело.


Пример css селектора.

Допустим есть вот такой компонент — search.customers.CustomersPaneComponent, для него селектор мог бы быть например таким — search-customers--customers-pane или seach-customers--customersPane

UFO just landed and posted this here

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


Генератор ts из view.tree уже есть так-то, правда код там страшненький. Или вы о чём?


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

UFO just landed and posted this here
А уже существует что-нибудь сложнее to-do list написанное с применением этого фреймворка? В последний раз, когда я пытался использовать FRP на продакшене, у меня получилась непонятная каша. Вероятно, это я не умею его готовить, но всё же хочется посмотреть рабочий пример, прежде чем делать второй подход к снаряду.
Вы не один :) У меня тоже с этим проблемы. Так же один из разработчиков популярной либы в этой теме реактивщины недавно поделился в личном общении о том что делать такую библиотеку конечно увлекательное занятие, но писать на таком реальные приложения у него так же мозг начинает кипеть.

Хотя есть всякие последователи штальца, у которых если что-то не «реактивное», то они смотрят на тебя как на гавно, может просто нужно какое-то другое мышление, которого нам не хватает :)

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


В $mol FRP и не используется. Тут OORP — с ним всё куда проще.

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

Это вполне реальное приложение. Правда ещё не доведённое до релиза (бэкенд ещё не готов, диайн не согласован).

Вы уверены что понимаете, что такое FRP? RxJS и другие это не FRP это просто реактивные композиции
Rx — таки FRP, правда смапленное на понятие потоков. По определению FRP — асинхронность + event-ы + функциональные операции (map/filter/fold/flatMap (bind))
Да ну? А как же постоянные значения во времени (Behaviors) Rx есть просто кузина для FRP. И еще, FRP имеет сенс только в языках с сильной системой типов https://wiki.haskell.org/Functional_Reactive_Programming
Ну с оговорками конечно, но в целом да. Behavior — принцип, но не определяющая. И я тут скорее о скале говорил нежели о жс.

А за ссылочку спасибо — почитаем-с. Не сталкивался я ни с чем в хаскеле приближенным к реальности, хоть и делал набеги :)

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

Я уверен, что в этом терминологическом споре мы не придём к консенсусу. Однако, $mol_atom имеет модель реактивности, которая совершенно точно не относится к "функциональному программированию", ибо предполагает изменение состояния и побочные действия. Зато свойства объектов реализуются как идемпотентные функции, что позволяет писать простой и лаконичный код, не ломая себе мозг монадами.

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

Из близкого по теме рекомендую посмотреть: Binding.scala для понимания тру-реактивности (требует наличия свежей jdk и scala + sbt), elm для понимания ФП и Rx%LANGNAME% для понимания реактивных потоков (советую язык со строгой типизацией)
Работа с Bacon.js и семестр Prolog'а в универе не канают?

З.Ы. Пролог приплёл для красного словца, знаю, что он декларативный, но не функциональный.
Я скорее имел ввиду haskell, scala, akka/akka-streams (кстати есть порт на scala.js), spark и rx.

Суть в понимании какие плюсы даёт ФП и ФРП: понимание что такое монада (в первом приближении) и зачем они нужны; понимание как разложить любой SQL запрос на map/flatMap (bind)/filter/fold; понимание какую выгоду можно поиметь от генераторов в js помимо бесконечных списков; понимание чем являются генераторы js и чем будут являться async/await; понимание Promise, не в том смысле как пользоваться, а на чём он основан.

P.S. В целом конечно можно представлять ФРП и ФП как магию и при этом пытаться мыслить в категориях потоков данных, но ИМХО это проблематично.
Я тут ещё на одну проблему вашего тестирования хочу обратить внимание:
вот вы генерите 2000 записей чтобы вывести и получается что ленивая подгрузка будет полезна.
Но эта ситуация мало совместима с реальными задачами, потому что если эти 2 тыс. записей загружать аякс запросом и с какого-нибудь мобильного устройства, то сразу же выясниться что они так долго загружаются, что разницы какой фреймворк и за сколько эти данные отобразит уже не будет иметь значения (потому что будет отличаться в разы).

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


Обычный фреймворк:


  1. Делаем запрос за 2000 записей и ждём пол секунды.
  2. В течении 20 секунд загружаем данные.
  3. Наконец, показываем интерфейс.
  4. Скорее всего нужный пользователю элемент находится в первой десятке и он переходит к нему.

Итого: заставили пользователя ждать 20 секунд, потратить трафика на 2000 записей, потратили батарейку на 20 секунд работы.


Ленивый фреймворк:


  1. Сразу показываем интерфейс.
  2. Делаем запрос за 100 записями.
  3. Через полторы секунды уже показываем пользователю список.
  4. Скорее всего нужный пользователю элемент находится в первой десятке и он переходит к нему.

Итого: пользователь ждал всего полторы секунды, трафика потратили в 20 раз меньше, батарейку почти не потратили.


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


Обычный фреймворк: спустя 21 секунду пользователь понимает, что нужного элемента в верху списка нет и применяет фильтры, на что у него уходит допустим 5 секунд + 1 секунда загрузки нового списка, данные элементов уже есть. Итого: 27.


Ленивый фреймворк: спустя 2 секунды пользователь понимает, что нужного элемента вверху списка нет и применяет фильтры, на что у него уходит допустим 5 секунд + 1 секунда загрузки нового списка + 2 секунды загрузки данных первых 100 элементов. Итого: 10 секунд. Почти в 3 раза быстрее.

Делаем запрос за 2000 записей и ждём пол секунды.
Делаем запрос за 100 записями

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

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

Спасибо, что держите нас в курсе, как оно, на самом деле в мире веб разработки. :-)


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

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

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


wrike.com — частично серверная, частично клиентская сортировка/группировка/фильтрация. Когда вы меняете задачу — она тут же встаёт в нужное место списка задач, если хватает данных. Если не хватает — список перезагружается в фоне, чтобы узнать как её правильно расположить.

смотрел ваше выступление на РИТ, прочитал статью (прочитал, не просмотрел).
пара слов, чтобы не зря.

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

tree — это пипец, извините. я всё понимаю, но я прочитал его описание и все равно ни черта не понятно. на кой экономить символы, если оно компилится?
если это просто описание графа объектов, то для этого не нужен новый язык, каким бы замечательным он не был. поддерживайте json, а потом, для тех кто втянулся, может и tree, не знаю.
даже в react jsx не обязателен. там люди понимали, что нельзя так сразу.

этот tree ведь компилится в билд-тайме? или нет? у вас статье сказано, что при первом запуске, но это сойдет только для демо. надо показать как это сбилдить, чтобы прикрутить к своему бекэнду (который совсем не на ноде может быть, ясно дело).

непонятно где html. ну компоненты, хорошо, а html где? ни в одном из примеров я не вижу html/css кода.
я хочу виджет из бутстрапчика вставить и что?
нельзя презентовать фреймворк для веб вот просто так, в вакууме. в самом первом туториале должно быть понятно как прикрутить к своему бекэнду и как интегрировать существующие js-контролы (jq и т.п)

зачем долллары в именах прикладных классов?
ну это реально очень странно выглядит:
namespace $.$mol {
    export class $mol_app_supplies_positioner extends $.$mol_app_supplies_positioner  {}
}


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

мое имхо: с синтаксисом tree никто пользоваться этим не будет. извините, если что.
tree — это пипец, извините. я всё понимаю, но я прочитал его описание и все равно ни черта не понятно. на кой экономить символы, если оно компилится?
непонятно где html. ну компоненты, хорошо, а html где? ни в одном из примеров я не вижу html/css кода.

Проще работать с меньшим числом синтаксических конструкций, чем с большим для описания одного и того же. Синтаксис view.tree очень простой. Он несравненно проще, чем синтаксис похожий на html, похожий на xml или похожий на json. В $mol используются идиомы, которые очень плохо ложатся на html. Сам принцип построения приложений меняется при переходе к "компонентной архритектуре", где во главу угла ставится уже не "оживления вёрстки", а "композиция компонент". view.tree разрабатывался, чтобы как нельзя лучше соответствовать используемым идиомам. Да, можно было бы пойти 3 путями:


  1. Натянуть ужа на ежа и сделать громоздкий html подобный синтаксис. Хоть он и был бы похож на html, но это всё-равно был бы другой синтаксис, который нужно было бы изучать. Что бы там ни говорили, а JSX и angular-tamplates — это не html и *не js**, это отдельные синтаксисы со своими особенностями, которые необходимо знать, иначе вы ничего не сделаете. Вы бы точно также испытывали когнитивный диссонанс от непривычности этого синтаксиса. При этом синтаксис получился бы громоздким, неудобным и (самое главное) не последовательным, что усложнило бы его понимание и дальнейшее расширение.


  2. Отказаться от необычных идиом (всё есть компоненты, компоненты живут в свойствах других компонент и умирают, когда никому не нужны, компоненты взаимодействуют друг с другом через реактивные связи). Но тогда мы бы потеряли львиную долю преимуществ $mol. А в результате получился бы "ещё один ангуляр" или "ещё один реакт". Но зачем такой фреймворк нужен, если уже есть эти ангуляры и реакты и сотни их клонов?


  3. Отказаться от подражания HTML, который никогда не предназначался для композиции компонент, да и не подходит он для этого (и у пресловутых веб-компонент до сих пор нет реактивных связей, без которых любая композиция быстро разваливается). Да, мы получам хейт от тех, кто не хочет изучать "ещё один синтаксис", которые хотят "вот тут вхерачить див", не думая о поддерживаемости, переиспользуемости, компонуемости, кастомизации, инвариантах и прочих вещах, о которых вспоминают, когда уже поздно рефакторить огромную кучу… кода.

Мы выбрали последний вариант, при этом добавив некоторые гарантии на уровне синтаксиса. Такие как:


  1. Контекстно зависимое BEM именование элементов.
  2. Возможность извне заменить любой элемент внутри блока.
  3. Уникальное имя каждого элемента в рамках блока.
  4. Уникальный человекоконятный идентификатор каждого дом-элемента.
  5. Быстрый доступ к любому экземпляру компонента через консоль.
  6. Возможность провязывать друг с другом любые свойства любых компонент.

даже в react jsx не обязателен. там люди понимали, что нельзя так сразу.

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


этот tree ведь компилится в билд-тайме? у вас статье сказано, что при первом запуске, но это сойдет только для демо. надо показать как это сбилдить, чтобы прикрутить к своему бекэнду (который совсем не на ноде может быть, ясно дело).

Разумеется тянуть дев сервер на продакшен не нужно. Всё можно сбилдить одной командой в консоли npm start пусть/к/модулю Результат можно выложить на любой статический сервер.


я хочу виджет из бутстрапчика вставить и что?

Лучше всего обсудить это ваше желание в отдельном issue. Тут могут быть несколько вариантов:


  1. Я расскажу, как лучше всего интегрировать интересующий вас виджет.
  2. Я реализую универсальный компонент для быстрого прикручивания любых бутстрап виджетов.
  3. Я смогу убедить вас в ущербности бутстрапа и мотивировать воспользоваться иными более адекватными решениями.
  4. Я реализую недостающий функционал в стандартной библиотеке компонент, которым вы сможете воспользоваться, не прикручивая всяких бутстрапчиков.
  5. Я расскажу вам как легко и просто реализовать нужную вам функциональность родными средствами фреймворка.

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


зачем долллары в именах прикладных классов?

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


ну это реально очень странно выгляди

Согласен. Есть идеи как это можно было ещё упростить?


общее впечатление после ваших ответов — избегать.

А как правильно отвечать, чтобы "продавать"?

тут как-то немного все в кучу получается. html и не-html это одно, а синтаксис для описания графа компонентов другое.
ок, вы выбрали подход «все есть компонент». т.е. html вообще нет, так? но ведь это автоматически не означает, что надо изобретать новый синтаксис.
помню был такой фреймворк — SmartClient, там UI описывался json'ом из компонентов.

Да, мы получам хейт от тех, кто не хочет изучать «ещё один синтаксис», которые хотят «вот тут вхерачить див»

вы так пишите, как будто это что-то плохое ))
я боюсь, что не хотят изучать «ещё один синтаксис» вообще все, кроме тех, кто его создает.
это нормально и зачем так реагировать.

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

я извиняюсь, а почему «вхерачить див» сразу приводит к «кучи говна»?
мне для каждого дива надо компонент сочинять? довольно странно.
получается, что вы полностью скрываете платформу. очень спорно.

Я смогу убедить вас в ущербности бутстрапа и мотивировать воспользоваться иными более адекватными решениями.

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

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

это я не очень понял. у кого патологическая боязнь?

Есть идеи как это можно было ещё упростить?

я бы рад, но я не понимаю как это работает вообще

А как правильно отвечать, чтобы «продавать»?

ну стараться прислушиваться что ли…
в плане завоевывания популярности побеждает же не то, что лучше (правильней). а по каким-то другим причинам. пример тому фейсбук.
поэтому, если 100 человек скажет, что «неудобно», ну значит неудобно.

p.s. я не видел, чтобы в сравнениях вы упоминали Ember с его Glimmer'ом. там тоже вполне себе реактивность теперь. компоненты, tracked декоратор. при этом есть html.
ок, вы выбрали подход «все есть компонент». т.е. html вообще нет, так? но ведь это автоматически не означает, что надо изобретать новый синтаксис.

Тут можно вспомнить про гипотезу лингвистической относительности: формат описания определяет архитектуру. Что похоже на хтмл — будет и использоваться как хтмл — отсюда и желание "вкорячить див с катомным классом", вместо добавления семантически именованного компонента с нужным лейаутом. Отсюда же и поиск элементов через css-селекторы, вместо того, чтобы просто взять их из свойства компонента. Отсюда же и невозможность извне как-то этот див поменять без плясок с бубном. Отсюда возможность стилизовать компонент в контексте другого компонента лишь через каскад, а не уникальное семантичное имя. Отсюда конфликты имён и сильная завязка стилей на структуру компонента.


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

Про бутстрап я ответил чуть ниже. Сторонние библиотеки подключать приходится, но только тогда, когда не хватает родных. А у сторонних есть следующие беды:


  1. Как правило большой вес Например, могут притянуть с собой jq, который сам по себе весить может больше, чем всё приложение на $mol.
  2. Как правило они не реактивны, что влечёт проблемы синхронизации. Тут требуется написать реактивную обёртку.
  3. Как правило они плохо настраиваемы. Даже мегакомбайны с развесистыми конфигами, часто не покрывают всех потребностей по кастомизации.

это я не очень понял. у кого патологическая боязнь?

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


я бы рад, но я не понимаю как это работает вообще

Из view.tree писания ренерируется класс в неймспейсе $. Когда нам надо добавить поведение, то мы пишем view.ts с одноимённым классом но в неймспейсе $.$mol, который наследуем от сгенерированного. В рантайме оба неймспейса ссылаются на один и тот же объект (по умолчанию — глобальный). Это позволяет сделать view.ts опциональным, не потеряв статической типизации. Проще способа объяснить TS компилятору "если такой-то класс (с расширенным интерфейсом) не определён — используй другой", я пока не нашёл.


если 100 человек скажет, что «неудобно», ну значит неудобно.

Думаю, если бы эти 100 хотя бы попробовали, они бы так не говорили :-)


я не видел, чтобы в сравнениях вы упоминали Ember с его Glimmer'ом. там тоже вполне себе реактивность теперь. компоненты, tracked декоратор. при этом есть html.

На первой же странице чудесный пример:


<span data-name={{concat firstName (
if lastName (concat " " lastName) "Doe"
)}}></span>

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

Что похоже на хтмл — будет и использоваться как хтмл

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

Про бутстрап я ответил чуть ниже.

холиварчик. опустим это.

Сторонние библиотеки подключать приходится, но только тогда, когда не хватает родных.

с точки зрения автора фреймворка (a framework) следует предполагать, что пользователи подключать сторонние библиотеки будут всегда. а не объяснять как это плохо. это не интересно. то, что сторонние библиотеки плохо интегрируются — это проблема фреймворка.

это я не очень понял. у кого патологическая боязнь?

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

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

Много ли вы найдёте крупных проектов с «персональной бесплатной поддержки от мейнтейнеров»?

насчет «персональной» не скажу. но ментейнеры Ember'а сидят в Slack-чате и отвечают на гитхабе весьма оперативно.

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

это не преподносится как киллер-фича. это логика в шаблонах. кто-то любит, кто-то нет. это синтаксис Handlebars. проверенный временем и поддержкой синтаксиса в куче IDE (чего кстати не предвидится для .tree). Ember и Glimmer не в этом.

в общем я не спора ради затеял. а просто поделился впечатлением «мимо проходящего». вроде интересно, но глубже вникать почему-то не хочется.
вкратце резюме вот почему:
  • tree это пипец. сделайте что-то, чтобы людям не надо было вникать в это. может быть как advanced фича, для обращенных
  • перестаньте хаить другие фреймворки. это конечно очень по-русски, но так не делается. посмотрите на vuejs. тоже один чувак в начале написал и пришел когда поляна уже была поделена. и ничего, взлетело. но там никто не хаял реакт. в документации есть отдельный раздел со сравнением с каждым популярным фв, но там их не ругают
  • то, что нет html во вьюхах — это очень стремно. я не готов спорить об этом. но уверен, что оттолкнет многих. в любом случае это надо объяснять специально и явно
  • интеграция сторонних конролов внутрь. D3? какой-то датагрид? гугль-мап? все надо. должно быть понятно как это делать
  • интеграция в другую сторону: как сделать маленький виджетик на моле и вставить его в большое приложение (со всеми щами типа передачи туда и обратно данных). тогда «подсесть» будет намного проще

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

Именно это и делают JSX и styled components. Вас это не смущает?


то, что сторонние библиотеки плохо интегрируются — это проблема фреймворка.

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


если никому не нужно, то почему нужно мне?

Вот и получается, что нет комьюнити, потому что нет комьюнити. Как разорвать этот порочный круг?


перестаньте хаить другие фреймворки

Я говорю как есть: Реакт — сборище архитектурных антипаттернов. А должен лицемерно говорить, что "реакт — классная штука"? Не на столько мне хочется продвинуть $mol, чтобы идти на сделки с совестью. Люди уже начинают потихоньку разочаровываться в Реакте и пересаживаться на новый поезд Вуй. Вот он — гораздо более практичная штука, чем хипстерский Реакт и интерпрайзный Ангуляр.


интеграция сторонних конролов внутрь. D3? какой-то датагрид? гугль-мап? все надо. должно быть понятно как это делать

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


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

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

Именно это и делают JSX и styled components. Вас это не смущает?

ну jsx все же нет, т.к. просто описывает граф.
а virtual dom да, и да, смущает.

пересаживаться на новый поезд Вуй

он не вуй, он вью. view по-французски.

По французски будет просто "Вю".

UFO just landed and posted this here
Дмитрий убедите меня в ущербности bootstrapa

https://gist.github.com/iAdramelk/d328b73c72cab92ef95f


Никто не сделал что-то подобное Transform Tool для дивов.

Я не в курсе, что это. Гугл не помог.


Я бы добавил контейнерность и возможность самосборок снизу вверх без использования специальных скриптов

Я не очень понял, как вы себе это представляете.

UFO just landed and posted this here

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

UFO just landed and posted this here

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

UFO just landed and posted this here

Ну так создаётся компонент (владелец), который кушает схему и данные и по ним компонует и настраивает вложенные компоненты. Так, например, работает markdown визуализатор.

UFO just landed and posted this here

Так схема может быть в любом формате. Просто читаем конфиг и в зависимости от значений в нём создаём те или иные элементы. Соответственно один раз реализуем компонент преобразующий конфиг в дерево компонент и можем менять конфиги как перчатки перестраивая интерфейс. Так, например, я сейчас реализую визуальный редактор компонент — он читает все view.tree и формирует интерфейс для настройки.

про фреймворк интересно!

Но было бы еще интереснее если бы разобрали более конкретные кейсы,
какие задачи и как реализуете на вашем фреймворке.
хех… надо обертку на ClojureScript сбацать :) в пику Reagent
Sign up to leave a comment.

Articles