Pull to refresh

Comments 216

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


<template>
    <img src="[[img]]">
    <p>[[text]]</p>
</template>

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

в React манипуляций со строками нет. В коде компонента вы пишете


render() {
   return <div>
     <h1>Hello!</h1>
   <div>
}

На этапе сборки произойдет конвертация JSX -> JS и код станет таким


render() {
   return React.createElement('div', null, [
         React.createElement('h1', null, 'Hello!')
   ]);
}

Получается обыная JS-конструкция, которая легко и быстро парсится браузером.


А поскольку React.createElement лишь генерирует JS-объект, который легче DOM-объекта, то все будет работать быстрее, но это уже другая история про React Virtual DOM.

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


И это уже не говоря о том, на сколько сгенерированный React сниппет будет медленней чем простой <template>, который браузер парсит напрямую, и то, что для <template> нет необходимости в системе сборки (всегда хорошо иметь необязательные вещи, это добавляет гибкости).

Но ведь мы то понимаем, что на чистом ДОМ, да еще оптимально, можно написать 1-2 критичных компонента, но не все приложение.

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


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

Если рассматривать сферический DOM в вакууме, то да, React == Shadow DOM + DOM, а потому медленнее.
Но на самом деле речь ведь о скорости и сложности, с которой DOM переводится из состояния A в состояние B.
Да, эффективное использование DOM всегда быстрее, но сложность в таком случае может расти очень быстро, если речь идёт о чем-то более сложном, чем обновление properties на узлах. И тут внезапно может оказаться, что толковый подход это уже половина реактовского dom reconciliation, только глючная и никем не тестированная.

Это зависит от многих факторов: иногда и правда дико сложно, иногда совсем нет.


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


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

В вашем примере template с "нативным" DOM есть конструкции "[[img]]" и "[[text]]". В других ваших примерах кода есть и посложнее: "[[isSelected, 'active', '']]".


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


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

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


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


Если соизволите сделать простой пример на React — с радостью портирую его на Polymer 1.x для сравнения.


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

Polymer замечательно хранит свою мета-информацию в properties, как и Peact в state. Между Polymer и React вообще вся разница, по большому счету, в том, что Polymer опирается на стандарты а React — на костыли.

Я знаком с внутренностями Poymer) Имел ввиду, что мета-информацию Polymer всё-таки при инициализации собирает, а мог бы собирать во время сборки, что позволило бы выжать ещё немного производительности.

Я знаком с внутренностями Poymer

это я понял, я говорю о нем именно в контексте сравнения с Реакт, о том, что после сборки проекта на Полимере vulcanize-ом никакого преимущества в плане производительности у Реакта нет: и там и там мы имеем все свойства в памяти и связывание с реальным DOM, при этом быстрее будет скорее Полимер из-за оптимизации работы браузера с веб-компонентами (пока конечно только в Хроме но скоро и в остальных браузерах).

>при этом быстрее будет скорее Полимер из-за оптимизации работы браузера с веб-компонентами (пока конечно только в Хроме но скоро и в остальных браузерах).

Быстрее чем простой жаваскрипт объект для компоненты в реакте?

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

А как вы пришли к выводу о том что нативная поддержка веб-компонентов позволит сделать какие-то оптимизации из-за которых оно будет быстрее чем использование простого жаваскрипт объекта?

DOM — это такая громоздкая штуковина со сложной иерархией, кучей собственных подписок на события, кучей эмиторов, и главное, жесткой связью всего этого с рендером в браузере. Прослойка в виде виртуального DOM — не избавляет нас от необходимости вносить изменения в DOM реальный, а потому, некорректно просто сравнивать скорость изменений абстрактного js-объекта с итоговым рендером всей этой кухни. А вот при работе с реальным DOM уже много чего возможно сделать и далеко не только в рамках js-рантайма, вспомните хотя-бы про css свойство will-change. Поэтому, при нативной поддержке веб-компонентов возможна более глубокая оптимизация.

Речь не про прослойки, которые занимаются изменением DOM'а, тк спека веб-компонент всё равно не предоставляет никакого датабайндинга, сейчас мы просто говорим про компоненты и оверхэд компонент.

Если вы не знали, то компоненты в реакте — это простые жаваскрипт объекты, которые не имеют никакого отношения к DOM. А вот нативные веб-компоненты — это уже громоздкие DOM объекты.

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

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

Реакт компоненты никак не отображаются в браузере, в браузере отображаются только DOM элементы.

Реакт компоненты — это простой объект + лайфсайкл методы. А вот веб-компоненты — это жаваскрипт объект + DOM объект + не самые быстрые вызовы из js -> native + тормозные вызовы лайфсайклов native -> js.
Реакт компоненты никак не отображаются в браузере, в браузере отображаются только DOM элементы.

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

Компоненты в реакте могут и не создавать DOM элементы: https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html#higher-order-components-explained

Так же как и в полимере не все веб-компоненты будут отображаться и видны пользователям: https://elements.polymer-project.org/elements/iron-ajax

И? Как это вообще связано со скоростью работы с DOM?

«при этом быстрее будет скорее Полимер из-за оптимизации работы браузера с веб-компонентами (пока конечно только в Хроме но скоро и в остальных браузерах).»

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

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

Ваша вера в то, что Реакту волшебным образом удается полностью избегать работы с реальным DOM мне кажется полным абсурдом. Когда вы примете тот факт, что это не так — вы сами придете к мысли, что говоря об оптимизации — мы говорим, прежде всего, об оптимизации работы с DOM. В любом случае, какой бы фреймворк или библиотека у нас не были "под капотом". Чистый js — работает быстро, заставить его заметно тормозить можно только специально стараясь. А говоря о работе именно с DOM — мы уже можем сравнить конкретные подходы. И в этих подходах, уверяю вас, нет никакой магии, все те-же обычные операции над нодами. Единственный способ сказать браузеру, что ваша нода чем-то серьезно отличается от других — использовать веб-компоненты, которые являются стандартом, в отличии от абстрактных, с точки зрения браузера, реакт-компонент. Все.

Вы так и не рассказываете про какие-либо оптимизации, которые возможны благодаря тому что веб-компоненты будут нативно реализованы в браузере в том виде, в котором они описаны в спеке веб-компонент по сравнению с простыми react-like компонентами :) Хоть один пример можно?

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


Это всего лишь один из подходов, и странно видеть в нем решение всех проблем.

Ещё один :) Речь про реакт-компоненты и веб-компоненты, и какие-то мифические оптимизации веб-компонент.

Это не имеет никакого отношения к тому как реализован дата биндинг в полимере в традиционном angular1 стиле, или в реактовом с использованием виртуального дома. Вообще никакого отношения, потомучто веб-компонентам насрать на то как будут вставляться, удаляться или обновляться веб-компоненты.
i360u с ними спорить бесполезно.

Им нужны технологии, которые позволяют не думать и ни за что не отвечать. :)
Все то, что делает эта ерунда, можно реализовать с помощью дополнительной пары строчек кода, когда появились тормоза.
Разве тут кто-то спорит? Я лишь хочу узнать что это за «оптимизации работы браузера с веб-компонентами (пока конечно только в Хроме но скоро и в остальных браузерах).» Больше меня ничего не интересует :)
Ничего в них громоздкого нет, это те же самые JS-объекты.

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

На самом деле, <template>, который браузер отпарсит в DOM, а мы потом размножим и удалим шаблон, будет намного медленней. Это была одна из проблем Angular 1, которую Angular 2 решает с помощью AoT компиляции шаблонов. Весь смысл Virtual DOM в том, что браузер очень медленно работает с DOM деревом, поэтому нужны костыли. http://stackoverflow.com/questions/28857074/angular-compile-is-slow-how-to-avoid
Маленькое уточнение — это когда мы работаем реально с DOM. Если у нас неприбинденный элемент или фрагмент — нет никаких тормозов, и чем это настолько отличается от виртуального DOM что появляется необходимость его вводить — лично мне непонятно.

В React все компоненты определяют метод render, который возвращает html исходя из актуального своего состояния.
Проблема в том, что этот метод в React вызывается на каждое изменение стейта. С одной стороны это преимущество, нам не нужно думать о том, как это изменение стейта отразится на html, у нас есть функция render.
Если бы render возвращал каждый раз настоящие DOM-элементы, это было бы медленно. Например, мы поменяли один class на active, а нам вернулся весь наш html.


Virtual-DOM является легкой прослойкой. Его объекты ничего не весят, поскольку не имеют ни размеров, ни подписчиков на события, это просто данные типа JSON. Его можно создавать много и дешево, а из него уже выяснять, что именно нам нужно обновить в реальном DOM

Это понятно, но это ведь следствие кривизны реакта, не?

>Весь смысл Virtual DOM в том, что браузер очень медленно работает с DOM деревом, поэтому нужны костыли.

Пишем в dom, биндим со стейтом и смотрим как броузер сам перерисовывает что надо. Какие могут быть подводные камни (без стеба, реально интересно мнение практика. Я сейчас подобную схему реализовываю, интересно мнение людей набивших шишек)

Ну такой вот другой подход на декларативные шаблоны.


Есть подход React с созданием VDOM на каждое обновление и последующим вычислением diff.


А есть подход Angular и Polymer которые завязываются на dom-ноду, парсят байндинги и пытаются слушать изменения и на них реагировать. Проблема этого подхода в том, что обслуживание DOM-ноды дорогое (не создание её в нативном API, а вся эта окружающая обвязка по разбору директив). Соответственно, чем больше нод, тем тормознее страничка.


А у React на ноды не завязано ничего, ограничивающим местом является размер VDOM, а он легче и в него влазят бóльшие объемы данных. Поэтому я выбираю React.


Кроме того, у React есть метод shouldComponentUpdate, который может не допустить обновление части поддерева, сделав страничку отзывчивее. Как сделать такое в Angular/Polymer, я не знаю.

Ангулар/Полимер и без всяких shouldComponentUpdate не будут обновлять поддерево.

Угу. Даже когда надо бы обновить, они не будут. Т.к. Angular следит за изменениями через shallowCompare. Как только у нас массив или объект с свойствами, здравствуй закат солнца вручную через ngDoCheck, причём её надо выполнять очень быстро, т.к. она будет вызываться при абсолютно любом событии и много раз за цикл rerender. А раз у нас всё вручную, то можно и забыть случайно обновить. Типа вот так https://github.com/primefaces/primeng/pull/986/

Это только во втором Ангуляре.

я о том, что в Angular нельзя сказать "внутри этого компонента ничего не поменялось, watchers вызывать не надо", а в React прекратить вызовы render дальше по дереву – можно

>А есть подход Angular и Polymer которые завязываются на dom-ноду, парсят байндинги и пытаются слушать изменения и на них реагировать.

Я тут слегка про другое говорю. Вот есть у нас аттрибут или содержимое тега (текстовая нода). Мы разбираем jsx (без реакта) и видим что тут не скаляр кладется в атрибут или ноду а биндинг на стейт. (это в нативном кастомном элементе) Через Proxy вешаемся на стейт (это все в момент создания элемента) и в случае изменения объекта/переменной меняем содержимое атрибута/ноды. То есть перерисовку вешаем на броузер. Могут быть в такой схеме проседания по сравнению с реактовской моделью, как считаете? (то есть как я вижу — тут как раз никакого оверхеда в виде инфраструктуры это шаманство поддерживающей — нет, все нативненько, но реальность часто со мной не соглашается)

React компоненты — это и есть View + поведение. Как в PHP принято выносить в шаблоны View какое то поведение включающее в себя незамысловатое ветвление if, else, так собственно и здесь. За одним исключением — добавляется какая то минимальная реакция на события. Важно понимать границы где заканчивается View и начинается бизнес-логика.

Импользование template string вместо <template> элемента значит, что вы игнорируете возможности платформы, которые могут помочь вам распарсить кусок DOM заблаговременно, тем самым увеличив производительность.


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

Разница в 8 строк. Но если в первом случае когнитивная нагрузка при работе с файлами небольшая, то с JSX (имхо) когнитивная нагрузка сильно возрастает. К тому же, сама разница в количестве строк по большей части обусловлена самим React. К примеру, в Polymer подобное:


{{#if isOverLimit}}
    {{warningText}}
{{/if}}

я решил кастомным методом if:


[[if(isOverLimit, warningText)]]

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


<div class="cart-item {{#if isSelected}}active{{/if}}">
<div class="cart-item ${isSelected ? 'active' : ''}">
<div class$="cart-item [[isSelected, 'active', '']]">

<button class="btn-more" {{#if isOverLimit}}disabled{{/if}}>+</button>
<button class="btn-more" ${isOverLimit ? 'disabled': ''}>+</button>
<button class="btn-more" disabled="[[isOverLimit]]">+</button>

Синтаксис сравним по читаемости к template string, такой же краткий, и при этом в HTML формате, позволяя использовать для обработки возможности платформы.


А ещё можно писать не HTML, а Pug/Jade, где читаемость становится ещё лучше (нужно знать синтаксис, но мне кажется, зная CSS вам не составит труда понять что тут происходит):


div(class$="cart-item [[if(isSelected, 'active', '']]")
  h3 [[title]]
  img(src="http://[[img_url]]")
  div
    button.btn-less -
    input(value="[[count]]")
    button.btn-more(disabled="[[isOverLimit]]") +
  | [[isOverLimit, warningText, '']]

Если в ход идут миксины, тогда разница становится ещё больше.


В итоге, мне кажется, JSX делает всё только хуже, не позволяя использовать сторонние инструменты вроде Pug/Jade и, по сути, больше решает проблемы синтаксиса самого React.

Добрый день!


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


Теперь давайте про конгитивную нагрузку.


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


[[if(isOverLimit, warningText)]]

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


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

где весь html короткий

Любой HTML слишком длинный, чтобы его писать.


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

В React тоже есть синтаксис шаблонизатора, который надо знать, пусть его не так много, как в каком-нибудь handlebars.

Синтаксиса шаблонизатора по сути не существует в React. Весь синтаксис есть Javascript и объекты Javascript, DOM объекты.

Есть некоторые вещи, которые в Jsc/react мешают — className, style={}, id, key… и невозможно использовать зарезервированные ключи (with, while, var, break), хотя их и не так часто потребуется использовать.

Само собой, JSX продиктован тем, как устроен React, и в контексте React он может быть предпочтительнее.
Опять таки согласен с коротким HTML, в этом случае может быть вполне резонно, но даже сниппет в статье как по мне уже больше оптимального, для внедрения в JS, размера.


Jade сам по себе может сослужить достаточно мощным DSL, без ущерба читаемости.
Вот сравните jade (379 байт) с html (2176 байт) (желательно отформатировать, чтобы увидеть масштаб преобразований).
Там форма с 7 параметрами конфигурации и в Jade это легко понять. Не сложнее устроен и лежащий рядом JS файл, который использует общие с другими формами абстракции. Но HTML как раз получается достаточно объемным.


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


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

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


И то, что это именно синтаксис, накладывает серьезные ограничения. Поддержка Dart, CoffeeScript, ClosureScript, HAML, Pug/Jade? Даже если есть компилятор, не факт, что IDE/редактор не сойдет с ума.


В этом отношении подход Vue.js гораздо изящнее. *.vue — опциональный формат (и не такой опциональный, как JSX, потому что писать React.createElement('div') никто в здравом уме не будет), и это просто html. Шаблоны, стили, код — все это пишется на любом языке и при необходимости (разрастание, верстальщик без специальных познаний) легко выносится в отдельные файлы.
При этом Vue.js умеет в Redux с вытекающими плюшками.


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

Я возьму Ваш комментарий в копилку IDE-зависимостей, в холиварах vim vs IDE помогает )

Я смотрел на Vue и Polymer, который использует такой же подход html-модулей. Но у меня как-то не зашло.


Когда у вас на первом месте html, то и импорты зависимостей производятся в html. Но javascript-функции с логикой надо импортировать в JS. В результате у меня сложилось ощущение дискомфорта от разнородных импортов.


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

Странно, у меня во Vue как раз JS-first и входная точка именно JS. HTML я не импортирую. Vue отвечает только за вью, простите за каламбур, как и положено:) За Polymer не скажу.

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

Парсинга чего? Все равно Polymer потом сам парсит эти строки чтобы вставить в них данные. Про какую производительность вы распинаетесь 10 комент подряд если не делали бенчмарков.

В контексте статьи я говорю о производительности <template> и других нативных интерфейсов вроде непосредственной работы с DOM (или максимально приближенной к ней).
К примеру, сравнение использования <template> по сравнению со строкой для Shadow DOM: https://rawgit.com/ebidel/e9bcb6fc88b40fc26ed9e768f7d19961/raw/template_vs_innerhtml.html
Можете добавить тест с императивным созданием элементов что будет ближе к React и посмотреть на результат.

Производительность DOM тут не главное, тут главное общая производительность (и библиотек и программиста который это все обслуживает) а разница между 55 и 99 мс не такая и страшная
UFO just landed and posted this here
Особо нового синтаксиса JSX не представляет. И «учить» его намного проще, чем запоминать десятки кастомных конструкций в очередном фреймворке. Тем более, JSX — это не реакт, как принято представлять. Существует много библиотек, использующих DOM API вместо innerHTML, ибо это работает быстрее, особенно на мобильных браузерах. И для этих библиотек используют JSX, т.к. это позволяет писать высокопроизводительный код с удобством простого HTML
JSX просто удобен, и плевать на идеологию. мне например удобно делать так:

import {dom} from '../inc/dom';
import {WAElement} from '../core/';
  
export let login = function(dom: any){ 

    return class Login extends WAElement { 

        constructor(){         
            super();           
        }                      

        connectedCallback() {  
            
            let holder = this; 
            let form =         
                <form>         
                    <input name='login' placeholder='login' /><br />
                    <input name='pwd' placeholder='pwd' type='password' /><br />
                    <input type='submit' value='log me' /> 
                </form>        
            ;                  
            
            form.addEventListener('submit', function(evt: any){
                               
                let formData = new FormData(form);  
                holder.sendMessage('authorize', {formData});
                evt.preventDefault();           
            });                
            this.appendChild(  
                <div>          
                    { form }   
                    <x-auth-sign-up />              
                </div>
            );
        }     
    };    
}(dom);


Просто и удобно. Просто удобно. Чьи религиозные чувства это задевает?

у меня картинка не загружается, не могу ничего сказать

            this.appendChild(  
                <div>          
                    { form }   
                    <x-auth-sign-up />              
                </div>
            );

Вы же основную нямочку — удобный one-way data flow не используете
Если что — это не React ) Это объявление кастомного элемента. Я просто показал как удобно когда у тебя по ходу кода верстка в нужный момент становится переменной. Так то в реале весь смак в dom функции — как запилишь так и будет (и декларативный биндинг и state и события)
>Если что — это не React )

То-то я смотрю и не понимаю. ;-)
От ES6 шаблонов это отличается несколькими дополнительными кавычками и ${form} вместо { form }, JSX в этом случае выглядит как оверхед.
раскажите как вот так:
form.addEventListener('submit', function(evt: any)
сделать после объявления элемента в ES6 шаблоне?

Очевидно придется поработать с DOM напрямую.


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


Я правильно понимаю что React использует even delegation подоход? То есть зарегистрированный явным образом листенер (addEventListener) не добавится к указанному элементу, а просто зарегистрируется как обработчик в одном глобальном листенере топ уровня (на основе генерируемого id компонента или что-то вроде этого)?

>А React автоматически отключает подобные инлайн листенеры когда элемент уже не используется?

У реакта перманентный анус в нативными листенерами и евентами, поэтому в реакте никто так не делает — там есть свои велосипеды.

Мы же не реакт обсуждаем а использование jsx вмеcто шаблонизатора (или html-templates в моем случае). У меня в коде вообще реакта нет — это модуль-класс для регистрации кастомного элемента авторизации, то есть потом после регистрации форму авторизации можно воткнуть в любом месте одним тегом.
У меня в коде вообще реакта нет — это модуль-класс для регистрации кастомного элемента авторизации

Я вот и интересовался как потом этот инлайн обработчик отключается.

нативно, самим броузером. Создается такой же элемент как и div или form, то есть вот тут:
return class Login extends WAElement
мы наследуемся от WAElement который обвертка вокруг HTMLElement, то есть все наши кастомы — это обычные html элементы (теги, объекты, в завимости от контекста) и соотв. браузер работает с ними также.
То есть тут что важно понять — есть jsx, и есть реакт вокруг него (да, надо понимать именно так). Так вот реакт можно убрать и вставить свою функцию сборки дома. И уже от реализации этой функции будет зависеть что мы можем в jsx. У меня например (полусырой код, конечно но уже в принципе работает) идет биндинг если в аттрибутах или содержимом тега указан объект класса state. То есть не надо вообще явно ничего привязывать — в самой верстке указали объект — все, есть биндинг. Стейт элемента можно потом привязать к стейту приложения (в том числе выборочно через фильтры) — все, имеем тот же реакт но на кастомах.
Пример что я привел — довольно упрощенный, можно например функцию dom настроить так, что если значение аттрибута — функция, то это листенер на событие, имя которого совпадает с именем аттрибута. Тогда dom функция сама будет вешать листенеры, а в аттрибуте достаточно указать имя этой функции. Тоже удобно в некоторых моментах. В общем надо просто захотеть научится это готовить — и только потом уже решать, нравится это или нет (уже умея)
Оба представленные способы неправильные. :)
Первый — на ровном месте писать код, который ничего не делает.
https://hsto.org/getpro/habr/post_images/9fa/b88/49a/9fab8849a56fd96c3e4f02a998ecad37.png

Второй — шаблонизатор на ровном месте, когда можно обойтись нативным js.

Это как заставить дурака молиться богу. :)

A как бы вы оформили второй способ на нативном js?

1. Оказалось, под капотом там куча ерунды типа
https://habrahabr.ru/post/311226/#comment_9831732

Зачем?

2.
а) просто сцепил (сконкатенировал) бы строки, зачем усложнять? :)
б) тут иногда говорят, что типа сервер может возвращать для скорения уже откомпилированный шаблон/готовый html.
Капец, а не проще с помощью jquery ajax-ом дернуть страничку / api и обновить нужный DOM?

Это псевдопрограммирование. :)

П.С.
Спасибо хабру, что при отрицательной карме нельзя вставлять код, очень удобно всем.
Наказали, так уж наказали.
>Капец, а не проще с помощью jquery ajax-ом дернуть страничку / api и обновить нужный DOM?

Краткий ответ — не проще.
Эх история-история… когда-то, давным-давно, был E4X, но не прижился. Время, шло, история сделал очередной виток выдав JSX (upd: о, так, у них об это и написано)

Видимо важно не только придумать стандарт, но еще и разработать библиотеку, которая этот стандарт использует на пользу себе и пользователям.

Была полная поддержка в FF, и это более 10 лет назад, я даже пробовал его, почему и запомнил, но балом правил ie5-6

и забавный факт, но видимо E4X устарел настолько, что ссылка со страницы про JSX ведет на какой-то совсем другой стандарт: Case for 120 mm HVD-ROM disk

На mdn ещё есть: https://developer.mozilla.org/en-US/docs/Archive/Web/E4X/Processing_XML_with_E4X
Просто на сайте реакта опечатались, 375 vs 357
Да кстати на сайте JSX все правильно (ссылка на 357) — а вот сайт Ecma почему-то автоматом редиректит с 357 на 375 O_o
UFO just landed and posted this here
Когда они стали писать HTML в JS, я молчал, я же не пишу HTML в JS.
Потом они стали писать CSS в JS, а я молчал, я же не пишу CSS в JS.
А потом они стали писать всё в JS, и уже не было никого, кто бы мог протестовать.

я бы написал про CSS в JS тоже, но у меня нет такого опыта.


Был бы рад если кто-то расскажет, чем это может быть полезно.

Ваш код можно переписать без единой строчки JS и отдать параллельно верстальщику, программисту и переводчикам:


$my_cart_position_viewer $mol_viewer

    attr * my_cart_position_viewer_selected < isSelected false

    childs /

        < header $mol_viewer
            childs / < title \

        < imager $mol_imager
            uri < imageUri \

        < counter $mol_number
            enabledDec < canRemove true
            value > count 1
            enabledInc < canAdd true

        < messager $mol_viewer
            childs < messages /
                < messageOverflow @ \You have reached the limit

Верстальщик добавит демонстрации компонента в различных состояниях:


$my_cart_position_viewer_demo $my_cart_position_viewer
    isSelected false
    title \Xperia XYZ
    imageUri \xperia.jpg
    messages /

$my_cart_position_viewer_demo_minimum $my_cart_position_viewer_demo
    count 0
    canRemove false

$my_cart_position_viewer_demo_long_selected $my_cart_position_viewer_demo
    isSelected true
    title \Computer with screen, mouse, keyboard and foot heater
    count 1

$my_cart_position_viewer_demo_maximum $my_cart_position_viewer_demo
    count 2
    canAdd false

$my_cart_position_viewer_demo_over9000 $my_cart_position_viewer_demo
    count 9001
    canAdd false
    messages / < messageOverflow

И застилизует:


[my_cart_position_viewer] {
    display: flex;
    padding: .5rem;
}

[my_cart_position_viewer_selected] {
    background: #fed;
}

[my_cart_position_viewer_header] {
    flex: 1 1 15rem;
    font-weight: bold;
    padding: .5rem;
}

[my_cart_position_viewer_counter] {
    flex: 0 0 2rem;
}

[my_cart_position_viewer_messager] {
    color: red;
    flex: 1 1 15rem;
    padding: .5rem;
}

А программист возьмёт доменные модели:


    export interface $my_product {
        name() : string
        available() : number
        thumbUri() : string
    }

    export interface $my_cart_position {
        count( ...diff : number[] ) : number
        product() : $my_product
    }

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


    export class $my_cart_position_viewer extends $.$my_cart_position_viewer {

        position() {
            return < $my_cart_position > null
        }

        title() {
            return this.position().product().name()
        }

        imageUri() {
            return this.position().product().thumbUri()
        }

        count( ...diff : number[] ) {
            return this.position().count( ...diff )
        }

        canRemove() {
            return this.count() > 0
        }

        canAdd() {
            return this.count() < this.position().product().available()
        }

        isOverflow() {
            return this.count() > this.position().product().available()
        }

        messages() {
            return [
                this.isOverflow() ? this.messageOverflow() : null
            ]
        }

    }

А тем временем переводчики переведут текст "You have reached the limit" на все языки мира, получив следующего вида json:


{
    "$my_cart_position_viewer_messageOverflow": "You have reached the limit"
}

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

Вам не кажется, что настолько много гибкости мало кому нужно? :)
Ещё бы оно не пользовало клятого убийцу мизинчиков $
Мы строим приложения на реакте с первых его версий.

Сначала не использовали JSX (typescript не поддерживал), и еще жив и развивается проект где его нет (т.е. там dom.div({ className: 'item'}).

Потом JSX пришел, и мы радостно начали писать на нём.

Потом пошли делать фичу в проект без JSX, и заплакали от счастья и радости.

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

JSX — в топку.

Начиная с версии 1.6 в Typescript появилась поддержка JSX. Достаточно создавать файл с расширением .tsx


Поддержка в редакторах тоже улучшилась, поэтому может вам стоит попробовать TSX еще раз.

Похоже ты не прочитал сообщение. Там написано: «Потом JSX пришел, и мы радостно начали писать на нём.».

У меня на JSX два весьма немаленьких проекта, и два немаленьких проекта — без. Без JSX — намного лучше.
А какие фичи недоступны в JSX? Single root element обходится тем, что можно вернуть массив, а что еще?
Всякие приятные синтаксические штуки JS недоступны:
var className = ...; return dom.div({ className }) 

var eventName = immidiate ? 'onKeydown' : 'onBlur'; 
return dom.div({ [eventName]: this.validate })


Всякие привычные удобняшки усложняются или невозможны вообще:
var element = isInline ? dom.div : dom.span; return element({ ... })

dom.div({}, isVisible && dom.span({}, "Hello")) // так можно и с JSX, но появляется лишний уровень скобок

var b = new BemDomFactory('my-list'); // несложный велосипед, создаем до компонента
// in render():
b.div({ element: 'header', mods: [isRed && 'red'] }); // class='my-list__header--red
На самом деле, всё, кроме последнего, легко делается на JSX

1 и 2 делаются через spread operator, он уже stage2 вроде бы.
<div {...{className, [eventName]: this.validate}}/>


Третье:

var Element = isVisible? <div/>: <span/>

<Element .....>

<div>{isVisible && <span>Hello</span>}</div>

Вполне сработает. Тут другой подводный камень:
var b = false
<div>{b}</div>

Выведет
<div></div>

а не
<div>false</div>

как можно было предположить. Так сделано именно для того, чтобы можно было писать {b && <div/>}
Последний пример, теоретически, можно сделать как

React = new BemDomFactory('my-list');
и потом
<div ...../>

Более того, spread в jsx и spread в js-объектах, это разные сущности. Чтобы это заработало


<div {...props} /> 

достаточно подключить babel-preset-react, stage-2 тут не нужен.

>spread в jsx и spread в js-объектах, это разные сущности.

О сколько нам открытий чудных… ;-)

Достаточно посмотреть во что превращается код после Babel


const props = {...source};
//станет
var props = Object.assign({}, source);

<div {...props} />
//станет
React.createElement("div", props);

Как видно, во втором случае никакого копирования объекта не происходит, он лишь передается дальше.

Делая так:

<div {...{className, [eventName]: this.validate}}/>


— ты используешь специальный JSX-синтаксис, который позволяет переключиться назад на JS-синтаксис, чтобы не пользоваться ограниченным JSX-синтаксисом для атрибутов :)

Остается сделать еще один шаг, и отказаться от JSX-синтаксиса для элементов.

Чем лично мне, как программисту (не верстальщику) удобен jsx.
Оговорюсь сразу, что использую тайпскрипт и tsx, но многие вещи справедливы и без тайпскрипта:


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


  • Удобная работа с children. Как в ангуляре удобно работать с содержимым компонента? Никак, только через боль и страдания, через парсинг строк, обработку дом-узлов и transclude. В Реакте это просто объекты, можно обрабатывать обычным кодом.


  • Расширение и декорация компонентов и классов компонентов. Метапрограммирование для бедных. Но лучше, чем в строковых и dom-based шаблонах.


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

Однако, jsx в чем-то неуловимо раздражает. Постоянно спотыкаешься на className. Зачем это было делать?!


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

Можно всегда вынести верстку (JSX) в сторону и дать доступ верстальщикам (при должной организации).

className нужен потому, что еще поддерживается браузер IE8, в котором class не может использоваться в качестве ключа в объекте


<div class="test" />

станет таким


React.createElement('div', {
  class: 'test' // IE не распарсит эту строку
})

Насчет разделения работы верстальщика и девелопера могу предложить подход presentational and container components. В presentational собирается одна верстка, а логика содержится в специальных container components, где верстки почти нет.

Ну хоть в JSX могли бы разрешить class. Хотя однообразие, конечно.

Сейчас покопался в интернете, нашел объяснение Бена, в чем дело:


мы смотрим на свойства html (типа el.className=...), а не атрибуты (el.setAttribute('class', ...)), если это возможно. Атрибуты это всегда строки, в то время как свойства могут быть любым доспустимым в Javascript значением, что дает больше гибкости в некоторых местах. Например, мы можем воспользоваться свойством classList которое больше вписывается в нашу модель, чем простой className. Сейчас React не поддерживает classList, но в будущем сможет. Так что при условии, что className ведет себя как соответствующее свойство html [ноды], имеет смысл придерживаться именования className
Читал — есть компании, где всё же верстальщики пишут JSX.
Потом программисты пишут уже код.

Так и живут. Пишут, что нормально. Притёрлись. ;-)

Angular 2 все Ваши пункты покрывает с горкой.

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

Что не так с модульностью в Ангуляр 1 (и причем зедсь вообще Ангуляр 1, модульность это базовая/абстрактная вещь)?

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

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

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


@Component({
    __filename,
    bindings: {_input: '< input'}
})
class Controller extends require('./../common/base-class') {
    static $inject = [
        require('./../common/crud-service').$name,
        require('./hr-expert-data-service').$name,
        '$scope',
        '$q'
    ];

    constructor(Crud, expertDataService, $scope, $q) {
        super({Crud, expertDataService, $scope, $q});
    }

@Component — самописная обертка в стиле второго ангуляра. $name во все сущности ангуляровские подставляется на основе имени файла автоматически (по переданному значению __filename). В базовом конструкторе {Crud, expertDataService, $scope, $q} (зависимости) линкуются в отдельное поле объекта.

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

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

Кроме этого имя функций должно быть минифицировано при сжатии JS.

Хотя исторически причиной отделения HTML и JS было разделение статики и динамики, что уже неактуально для рендеринга на клиенте

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

Когда я это писал, у меня в уме представлялась типичная CMS на PHP, в которой есть html-верстка с вкраплениями php-сниппетов. С другой стороны был Javascript-код, которым показывались анимации, галереи картинок и т.д.


Где здесь логика, где представление?

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

для чистых верстальщиков есть css.

Глупости. Я не сторонник наличия в команде чистых верстальщиков, мне удобнее когда в команде работают более хардкорные люди. Но все таки чистые верстальщики еще не вымерли, и в моем понимании это html + css + less/sass.

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


Вот статья, c пояснением этого подхода.

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

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


Как по мне, производительность у Реакта достаточная для большинства случаев, кроме того, есть еще запас по оптимизации. Конечно, он медленее идеального обновления ДОМ вручную.


Но, как по мне, он наиболее прямолинейный и предсказуемый, с точки зрения разработки, из всех фреймворков-лидеров. Плюс поддержка IDE у него самая лучшая. Также у Реакта наиболее "дешевое", с точки зрения производительности, дробление на компоненты — каждый компонент — это функция (либо класс), а в Ангуляре, например, это дополнительная куча watcher-ов.

Приехали.

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

Да ладно вам. В соседней теме на Ext JS разрабатывали, и не смущала производительность до поры :)

Ext JS — это супер-мощная штука. Несколько сот классов. Под 400!

Но вот чего-то задвинут на задворки ентерпрайза. :-0
Также у Реакта наиболее "дешевое", с точки зрения производительности, дробление на компоненты — каждый компонент — это функция (либо класс), а в Ангуляре, например, это дополнительная куча watcher-ов.

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


Как по мне риакт далеко не уникален, и это просто хайп вокрг него и ничего более. Кроме того он слишком усложняет дело со своим вирруальным DOM деревом, вычисляя что именно патчить в реальном дереве. Я бы лучше посмотрел в сторону Riot JS.

>Я бы лучше посмотрел в сторону Riot JS.

http://todomvc.com/examples/riotjs/

1. добавляем два итема в туду лист
2. переходим в Active
3. отмечаем первый элемент активным
4. наслаждаемся косяками RiotJS

Уже бежим переходить на такую замечательную библиотеку :)

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

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

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

https://github.com/riot/riot/issues/484 (тут суть не в производительности, а в том что они там просирают внутреннее состояние)

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

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


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

>Я бы сказал ценность виртуального DOM переоценена

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

Проблема в основном с тем что люди ассоциируют производительность виртуального дома с реактом, реакт просто первым реализовал эту идею, но его реализация очень медленая и сейчас достаточно сложно внести какие-то изменения в реакт чтобы его ускорить, придётся полностью его переписывать. Мы с автором Bobril'а ещё два года назад продемонстрировали как можно реализовать очень быстрый виртуальный дом, с тех пор на этих идеях начали появляться реализации React API, например Inferno, и еслиб реакт переписывали, то вероятно бы реализовали его так же как современные виртуал дом либы https://twitter.com/sebmarkbage/status/776148675592544256

Вот вот, от риакта осталось одно название.

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

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

>станет второй ангуляр, но разумеется только для серьезных проектов.

Серьёзные проекты делают на Ext.js — который не собирается сдавать позиции в своей нише.
Я читал это.

У них не серьёзные проекты. :-)

Если без шуток — они просто испугались Ext.js 6-й версии. Очень мощная штука.
Если бы было просто перепрыгнуть с 4-й версии Ext.js на 6-ю — вопросов бы не возникало — писали бы дальше на Ext.js.

А так да, очень много УЖЕ разработанных (в том числе сторонних свободных) компонентов для Ext.js 4-й версии просто отказываются работать и в 5-й и в 6-й версии Ext.js.

Отсюда все ноги и растут. Имхо.

Реакт не более чем инструмент, не понимаю, почему хайп вокруг него так завышает ожидания :)


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


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

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

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

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

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

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

Это уже интересно. К, примеру, имеем цикл, а в какой последовательности проводить итерацию? Вот на этот вопрос где даётся ответ? В JSX непосредственно или где?

XSLT с вами не согласится.
Конечно, это изврат и так шаблонизировать сложно, но возможно.


А обычные шаблонизаторы типа handlebars или jade производят на выходе такой же js-код, как вы бы написали сами. Так есть ли смысл прятаться за абстракцию шаблонизатора, если на JS+JSX неплохо выходит.

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


В JSX тоже можно при желании написать нечто вроде:


<ul>
    <Each set={ items } 
          renderItem=>{ this.renderListItem }>
    </Each>
</ul>

Только это лишнее усложнение.

А при чём тут бизнес-логика?


Я чуть выше привёл обстоятельный пример шаблонизатора без циклов и условий.

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

Основная беда JSX в том, что он страшно выглядит как шаблон. Он позволяет делать все что угодно, но простые вещи — if, for в нем смотрятся не очень. Вот примеры для сравнения, React и Angular2:

Условие:
<div>
    { this.state.loading  &&
        <div class="loading-icon"> </div>
    }
</div>

<div>
    <div class="loading-icon" *ngIf="loading"> </div>
</div>


Цикл:
<ul>
    { this.state.items.map(item => 
        <li >{item.name} </li>
    )}
</ul>

<ul>
    <li *ngFor="let item in items" >{{item.name}}</li>
</ul>

Причем, для цикла в реакте еще рекомендуют map выносить в отдельную функцию, чтобы совсем запутать свой шаблон. В англуяре шаблон выглядит как html, близкий к тому, что получится в браузере. Реактовский шаблон, как правило, далек от конечного результата, хотя бы потому, что куски html часто оказываются разбросаны по функциям, порядок которых определяется порядком их вызова (соответственно, он может отличаться от порядка объявления в коде). Так что, представить, каким станет html в реактовском компоненте — не так просто.

На эту тему есть вот такая страничка сравнения:
http://jeffcarp.github.io/frontend-hyperpolyglot/


Кроме того, обычно в React шаблонах не пишут долгие цепочки с this, а создают переменные заранее


const {loading} = this.state;
return <div>{loading && <div class="loading-icon"></div>}</div>

Получается не менее компактно, чем в Angular, да еще и более нативно. С циклом то же самое.


Элемент цикла можно не просто вынести в отдельную функцию, но и использовать ее как React-компонент


function ListItem({title}) {
  return <li>{title}</li>
}

function List({items}) {
   return <ul>{items.map(item => <ListItem item={item} />)}</ul>
}

React-компонентом может быть любая функция, возвращающая JSX.


Зачем вам нужно знать, каким станет html полностью? Вы работаете на своем уровне абстракции, дочерние компоненты отдельно, на их реализацию можно посотреть потом, если захочется

justboris > Зачем вам нужно знать, каким станет html полностью? Вы работаете на своем уровне абстракции, дочерние компоненты отдельно, на их реализацию можно посотреть потом, если захочется

Отлично написали.

PFight77 > Основная беда JSX в том, что он страшно выглядит как шаблон. Он позволяет делать все что угодно, но простые вещи — if, for в нем смотрятся не очень.

Эстетичный подход — это конечно хорошо. :-)

Но стоп, «простые вещи» в JSX есть простой(!) Javasript код.
Он(код) по определению не может выглядеть страшнее, чем «некия» странные *ngIf, *ngFor и им подобные.

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

От того, что Вы записали в одну строчку код более красивым не стал: )) И да, на практике объявлять кучу переменных в начале функции лично мне всегда лень. И всем моим коллегам. Да к тому же, появляется лишняя степень индирекции, чтобы понять откуда берется значение необходимо это отследить. Лишняя сложность для понимания и отладки.


Зачем вам нужно знать, каким станет html полностью?

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


Ну а если без громких фраз:


  1. Прежде всего для верстки. К сожалению, css не так прост. Часто требуется добавлять всякие врапперы, менять порядок элементов и прибегать к прочим подобным приемам, чтобы добиться нужного вида и поведения макета. Часто для правильной верстки отдельной области нужно контролировать отдельные ее части. Поэтому для верстальщика важно видеть html целиком, и иметь возможно легко его менять. Плохо здесь именно разбиение шаблона даже в рамках одного компонента, с разбиением на дочерние компоненты еще можно смириться. Хотя я всегда предпочитаю не плодить слишком много мелких компонентов. Я считаю, что под-компонент есть смысл выделять, если он содержит хотя бы десяток строк html и сколько-то логики (за исключением простых, часто используемых stateless).
  2. Для отладки. Допустим, тех же багов в верстке. Я нахожу через DevTools в браузере проблемное место в html, и хочу найти код, где данный html рождается. В react это становится проблемой, т.к. код разбросан по куче мелких функций. Хорошо если find all по имени класса получится.
  3. Для простоты восприятия. Сколько бы мы не говорили про модульность и гибкость, это все вспомогательные вещи. Главное — результат. Если мы генерируем html, результат — картинка в браузере. В конечном счете я хочу получить конкретный макет, конкретный вид сайта. И я хочу понимать, как эта картинка сформируется. Дробление шаблонов в React осложняет это понимание.

Это все справедливо, но не только для Реакта. В Ангуляре все то же самое, да, думаю, и в любом другом фреймворке тоже.


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

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

PFight77 >Дробление шаблонов в React осложняет это понимание.

Все во все времена пытались избавиться от «монолита», а тут вывод о том, что «дробление шаблонов в React осложняет это понимание».

«Неисповедимы пути твои… » ©

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

PFight77 > Я, как уже писал, сторонник компонентов среднего размера — достаточных для редуцирования сложности, но при этом не на столько маленьких,

Не боитесь остаться в одиночестве?: «1. Функции должны быть маленькими. Совсем маленькими.»
https://habrahabr.ru/post/310590/

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


В этом беда ReactJS, он рассматривает формирование HTML как алгоритм (часть JavaScript), а не как построение по шаблону. Отсюда и маленькие функции, как Вы верно заметили.

PFight77 > >В этом беда ReactJS, он рассматривает формирование HTML как алгоритм (часть JavaScript), а не как построение по шаблону.

В этом его фишка (революционная особенность). Имхо.
PFight77 >В этом беда ReactJS, он рассматривает формирование HTML как алгоритм (часть JavaScript), а не как построение по шаблону. Отсюда и маленькие функции, как Вы верно заметили.

Это не беда — это философия. ;-)

Да, очевидно что БОЛЬШОЙ монолит — это плохо.

Но вот вы ратуете за средний(!) монолит — ну, типа чтобы код на страницу влезал при минимуме прокрутки. — Да, это будет также более понятно чем мелко-функциональный код — ибо, и это очевидно, именно так и пишут Hello-World всякие — именно чтобы читающий ПОНЯЛ код не отвлекаясь на организацию кода, на разбивку кода на мелкие функции(тратя своё внимание не на понимание а на поиск взаимосвязей).

Но после осознания Hello-World начинаются… рабочие будни — и там новая философия — чем мельче тем лучше!

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

Вы же ратуете за продолжение использовать философию Hello-World и дальше. В этом ваша ошибка. Имхо.

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

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

Повторю за dema > Не-не-не… Ну чесслово, если angular и react еще выглядят хоть как-то понятно, то вот это очень похоже на язык K. :)

Вот, если там(angular и react) хоть отдалённо напоминает старый добрый HTML — то у вас НОВЫЙ язык.

Я думаю, что разработчики JSX могли предложить ЛЮБОЙ синтаксис своего JSX (там всё равно через транспиллер его надо прогонять), но, поразмыслив, остановились (пока?) на неком гибриде старого доброго HTML — Чтобы… не пугать девелоперов! имхо. :-)

Лучше бы попугали, чтобы не получалось таких перлов:


class MyApp extends React.Component {
    render() { return (
        <MyPanel
            head={
                <MyHeader
                    title="My Tasks"
                    closable={ true }
                />
            }
            body={
                <MyTaskList
                    assignee="me"
                    status="todo"
                />
            }
        />
    ) }
}

Для сравнения, то же самое на view.tree:


$my_app $mol_viewer
    childs /
        < panel $my_panel
            head < header $my_header
                title \My Tasks
                closable true
            body < bodier $my_task_list
                assignee \me
                status \todo
>Лучше бы попугали, чтобы не получалось таких перлов:

Так выше всё родное — XML код, уже лет 10 как привычен то всюду. ;-)
Опять же, в примере с React я тут вижу просто компоненту, у которой атрибуты тоже компоненты. На самом деле
<A>
  <B/>
</A>

эквивалентно
<A children={<B/>} />

ну или
<A children={[<B/>]} />

т.к. в children могут быть несколько элементов.

И это ОЧЕНЬ удобно.
Есть еще вариант, например, как сделано в react-bootstrap модальное окно.

<div className="static-modal">
    <Modal.Dialog>
      <Modal.Header>
        <Modal.Title>Modal title</Modal.Title>
      </Modal.Header>

      <Modal.Body>
        One fine body...
      </Modal.Body>

      <Modal.Footer>
        <Button>Close</Button>
        <Button bsStyle="primary">Save changes</Button>
      </Modal.Footer>

    </Modal.Dialog>
  </div>

Все эти Header, Footer, Dialog, это статические поля в классе Modal.
В методе Modal.render потом вручную фильтруются дети. Что-то наподобие const header = this.props.children.filter(child=> child.type === Modal.Header)
Так и я говорю — обычный привычный вид XML кода (с тегами и атрибутами).

Я согласен, что это удобно, но это даже близко не XML. И нет никакого смысла предпочитать этот синтаксис банальному:


class MyApp extends React.Component {
    render() { return (
        new MyPanel({
            head:{
                new MyHeader({
                    title:"My Tasks",
                    closable: true
                })
            },
            body:{
                new MyTaskList({
                    assignee:"me"
                    status:"todo"
                })
            }
        })
    ) }
}
vintage >Я согласен, что это удобно, но это даже близко не XML.

Тут вы не правы вовсе, — что несколько странно не видеть XML:
https://facebook.github.io/react/blog/2016/09/28/our-first-50000-stars.html

«Since about 2010 Facebook has been using an extension of PHP called XHP, which enables engineers to create UIs using XML literals right inside their PHP code.

...we created JSX by forking js-xml-literal, a side project by XHP creator Marcel Laverdet. JSX took its name from js-xml-literal, which Jordan modified to just be syntactic sugar for deeply nested function calls.»

Я согласен, в таких вырожденных случаях React.createElement будет, наверное, удобнее даже визуально. Но, когда это обычный html с минимумом кода, то jsx всё-таки приятнее.

В том-то и дело, что при компонентной декомпозиции места для сырого html практически не остаётся.

Вот в бутстрапе, да, сделали XML, но цена этому — "В методе Modal.render потом вручную фильтруются дети.". Предложите каждый компонент так костылять?


Ну и опять же, сравните с:


$my_staticModel $my_modal_dialog
    title \Modal title
    body / \One fine body...
    foot /
        < closer $mol_clicker childs /
            < closeLabel @ \Close
        < saver $mol_clicker childs /
            < saveLabel @ \Save changes
vintage >Предложите каждый компонент так костылять?

Есть отличный (не шучу) Ext.js

Там ООП реализовано а все 100% — там свыше 400 классов для всего(!)
Хотите наследуйтесь, перегружайтесь, миксируйте…

Ext.js был года как 3-4 назад очень даже популярен.

Но дело в том, что БЫЛ (хотя он и сейчас есть — 6-я версия), но в трендах Ext.js сошёл на нет.

Ибо был… не революционен! Имхо.
;-)

лучше бы шутили, ибо ExtJS — тот ещё bloatware. Именно это его и сгубило.

Тут на вкус и цвет. Я, вот, терпеть не могу Jade, YAML и всё, где пробелы — значимые символы.

Попробуйте рационализировать свою неприязнь :-)

vintage 4 >Попробуйте рационализировать свою неприязнь :-)

Она идёт изнутри. Я вообще выбираю языки и фрейворки исключительно по эстетическим соображениям. ;-)

Совершенство достигнуто не тогда, когда нечего добавить, а когда нечего убрать.

UFO just landed and posted this here

Работает. Можете сами попробовать.


Что такое "скобочные теги"? Тут вообще нет никаких тегов — только создание компонент из других компонент, что в перспективе позволит рендерить хоть в OpenGL.

UFO just landed and posted this here

А где вы видите у меня скобочные теги? Если вы о первом примере, то это JSX.

Это не то же самое. Т.к. в примере с реактом заголовок и тело панели — это, скорее всего, не просто содержимое панели, а может быть обернуто в теги, добавлены классы и т.д.

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


<div class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title">My Tasks</h4>
      </div>
      <div class="modal-body">
              <ul>
                      <li>TODO: Item</li>
              </ul>
      </div>
      <div class="modal-footer">
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

Сорри, реализовывать компонент не стал, это пример результирующей разметки. Как видим, появилось много обвязки и дополнительных элементов. Плюс ХТМЛ для списка задач добавился в modal-body.

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


В реализации на view.tree просто наследуемся от $my_panel и добавляем чего не хватает:


$my_modal $mol_panel

    attr *
        tabindex \-1
        role \dialog

    head /

        < titler $mol_viewer
            childs / < title \

        < closer $mol_clicker
            eventClick > eventClose null
            hint < closeHint @ \Close dialog
            childs < closeIcon $mol_icon_close

Стопочку тегов реализовывать не стал, ибо они лишние. А вот и использование:


$my_modal_demo $my_modal

    title \My Tasks

    body /
        < task1 $my_task_row title \Task #1
        < task2 $my_task_row title \Task #2
        < task3 $my_task_row title \Task #3
>В реализации на view.tree просто наследуемся от $my_panel и добавляем чего не хватает:

Странный новый дивный язык. — Зачем?

Затем, чтобы создание и использование компонент не вызывало боль.

>Затем, чтобы создание и использование компонент не вызывало боль.

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

Но взамен мы изобретаем НОВЫЙ язык. Зачем?

Затем, чтобы не писать километры кода.


    /// event * click > eventClick
    event() {
        return $mol_merge_dict( super.event() , {
            "click" : ( ...diff : any[] )=> <any> this.eventClick(  ...diff ) ,
        } )
    }

    /// contenter $mol_viewer childs / < content
    @ $mol_prop()
    contenter() {
        const next = new $mol_viewer() 
        next.childs = () => [].concat( this.content() )
        return next
    }

Мой опыт мне говорит, что иногда лучше писать чуть больше кода, чем разгадывать клинопись из <,/,$,@ и т.д.

Лучше один раз освоить 6 операторов, чем писать 6 строчек вместо одной.


< - односторонний биндинг
> - двусторонний
@ - локализуемая строка
\ - сырые данные
/ - список
* - словарь
# - произвольный ключ
$ - префикс имени компоненты
UFO just landed and posted this here

Нет.


Интуитивней, но набирать очень не удобно.


Какие вы предложили бы обозначения для этих штук?

UFO just landed and posted this here
babylon >Я по памяти пишу. Поэтому мог не всё вспомнить. Соряйте:)

У вас много букв. ;-)
UFO just landed and posted this here
babylon > На всех значков не хватит по любому.

Unicodе большой. Хватит по любому всем.
UFO just landed and posted this here
babylon > Ага, еще бы клавиатуру специальную для этого.

Это идея для Kickstarter
UFO just landed and posted this here

Какого типа contentchilds)? Есть ли возможность их обработать?

В данном случае childs — массив, а content — любого типа. Обработать в каком смысле?

Обработать в каком смысле?

Отфильтровать по типу элемента, по заданным свойствам, программно создать массив элементов и т.д.

Можно. Так, например, работает ленивый рендеринг — он отфильтровывает блоки, точно не попадающие в видимую область.

vintage >Затем, чтобы не писать километры кода.

К этому стремятся все. :-)
В языки программирования ПОСТОЯННО вводят новые конструкции позволяющие писать меньше кода. ;-)

И, я согласен, что DSL язык можно ЗАТОЧИТЬ для конкретной области более точно («острее») чем использование универсального(более «широкого» чем DSL) языка программирования — Это мы практически видим и на примере Angular (со своими шаблонами и тегами) так и на примере React (со своим JSX).

Но вот взлетит или нет этот «заточенный» DSL — этого никто не скажет. Имхо.

JSX и Angular же взлетели.

vintage >JSX и Angular же взлетели.

Верно. Но это НЕ отменяет того, что — но вот ВЗЛЕТИТ ИЛИ НЕТ этот «заточенный» DSL — этого никто не скажет.

А сколько НЕ взлетело то!

Поэтому нужно сложить руки и жрать либо один хайповый кактус, либо другой?

vintage >Поэтому нужно сложить руки и жрать либо один хайповый кактус, либо другой?

Нет, надо ускоряться. Имхо. ;-)

Если сейчас, бают, раз в день выходит новый js фреймворк, то надо бы каждые 4 часа, по крайней мере. ;-)

Я только хочу отметить — взлетит или нет не знает никто. Никто из людей.

И кто бы за js фреймворком ни стоял — это НЕ поможет — есть примеры, когда проваливались проекты фреймворков и языков и от MS и от Гугла.

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


Я соглашусь, что Реакт дает больше гибкости. Можно, например, вынести часть рендеринга в метод, и переопределить его в дочернем классе. Вопрос в том, как часто нужна такая гибкость? Все-таки, 80% веба это простой контент, без особых замороченных контролов.


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

PFight77 >То есть, Вы считаете, что в промышленной разработке все «настолько просто», что заботиться о читаемости кода не нужно?

Я хочу сказать что код приводимый с целью быстрее ПОНЯТЬ суть должен отличаться от кода когда СУТЬ УЖЕ ПОНЯТА.

В продакшене СУТЬ УЖЕ ПОНЯТА постоянным(рутинным) практическим написанием кода.
Код продакшена НЕ предназначен для быстрого ПОНИМАНИЯ сути.
Код продакшена должен быть рутинным кодом.

PFight77 > Все-таки, 80% веба это простой контент, без особых замороченных контролов.

Это пока, но вэб меняется — он становиться сложнее. Разве не так?

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

Вы идеалист. ;-)

В ES6 ещё можно так делать:
function Component(str, ...vals) {
  const props = vals[0];
  const children = vals[1];
  //...
}

function render() {
  const props = {};
  
  return (
    Component`${props}${
      Component`${props}${'nested'}`
    }`
  );
}
Sign up to leave a comment.

Articles