Я уверен, большинство из вас, кто использует react используют jsx. Благодаря своему лаконичному синтаксису jsx улучшает читабельность шаблонов. Сравните:
render() {
return React.createElement('div', { className: 'block'}, 'Text of block');
}
// vs
render() {
return <div className='block'>
Text of block
</div>;
}
Второй вариант даже в таком простом примере выглядит намного понятнее. Если усложнить пример, то разница будет еще очевиднее.
Чем плох jsx
Все бы хорошо, если бы jsx был бы стандартной возможностью javascript, но это не так. Для работы с jsx вам потребуется транспилятор. Решив использовать jsx вы навечно становитесь зависимы от транспиляции. Еще недавно, такая зависимость никого не пугала, так как для использования новых возможностей из ecmascript 2015 вам в любом случае необходим транспилятор. Но все меняется, уровень поддержки es6 близок к 100%
По крайней мере, в develop-окружении уже можно избавляться от транспиляции. Представляете, какие возможности это открывает? Не нужно при дебаге ковыряться в выходе babel, который многое изменил, не нужны source map, после изменения файла нет необходимости ждать, пока закончится пересборка. И jsx в данном случае будет главной помехой… Есть ли альтернативы jsx?
Альтернативы jsx
Стандарт ecmascript 2015 определяет тегированные шаблонные строки. Пример, выше можно переписать так:
render() {
return es6x `<div className='block'>
Text of block
</div>`;
}
Более сложный пример:
import Input from './input';
render() {
return <div className='block'>
<Input name='name1'
value={this.props.value}
{...this.props.inputProps}
checked
/>
{this.props.items.map(item => <span {...item.props}>{item.text}</span>}
</div>;
}
// преобразуется в:
render() {
return es6x `<div className='block'>
<${Input} name='name1'
value=${this.props.value}
${this.props.inputProps}
checked
/>
${this.props.items.map(item => es6x `<span ${item.props}>${item.text}</span>`)}
</div>`;
}
Как подключить
npm install --save es6x
Пакет es6x поддерживает разные движки. Среди них react, hyperscript (h), а также (по умолчанию) универсальный вывод в json вида:
{
tag: 'div',
attrs: {
className: 'block'
},
children: ['Text of block']
}
Чтобы использовать совместно с react нужно указать метод вывода перед первым вызовом (в одном месте проекта):
import React from 'react';
import es6x from 'es6x';
es6x.setOutputMethod(React.createElement);
Особенности пакета es6x
- размер в сжатом виде около 2 кб
- шаблоны кешируются — во время первого исполнения создается кеш, который используется при повторных вызовах
- решена проблема пробельных символов внутри шаблонов, которой страдает jsx:
return <div>
{'some text'}
{' '}
<b>strong</b>
{' '}
<i>emphase</i>
</div>
В примере выше, в jsx требуется добавлять уродливую кострукцию {' '} между словами "text", "strong" и "emphase" а в es6x этого не потребуется:
return es6x `<div>
${'some text'}
<b>strong</b>
<i>emphase</i>
</div>`;
Производительность
es6x поддерживает кеширование, благодаря чему, при повторном вызове с тем же шаблоном не происходит парсинга и вызов происходит намного быстрее. Повторный вызов по результатам тестирования в 10 раз быстрее первого (в случае универсального парсинга в json, в случае парсинга для react разница меньше — менее чем в 2 раза). Так же я производил сравнение с конкурентным пакетом t7 при парсинге для react. Результаты:
jsx-выход: 15683 ops/sec ±1%
es6x: 11187 ops/sec ±1%
t7: 10253 ops/sec ±1% (не поддерживает многих плюшек jsx)
То есть падение производительности около 30%. Что оказалось меньше, чем я ожидал. Объясняется тем, что метод createElement достаточно тяжелый.
Пользуйтесь, сообщайте мне о багах. Всем спасибо за внимание.
Комментарии (78)
norlin
09.12.2016 16:55+8Второй вариант даже в таком простом примере выглядит намного понятнее
Вот этот jsx – главное, что отталкивает лично меня от React. Всю мою сознательную дейятельность JS-разработчика вокруг твердили "нехорошо смешивать логику и представление". А тут вдруг – новая мода – давайте будет писать html прямо в js коде!
jMas
09.12.2016 17:00+6Потому что React-компонент — это и есть View, конечно его можно перемешать с данными, но это на усмотрение самого разработчика. Держите данные в сторе — это будет Model, а все представление в React-компонентах — это View / View. На вход подаешь данные — получаешь видоизмененное представление, все как в PHP templates с
if
,foreach
.norlin
09.12.2016 17:23-4render() { return <div className='block'> Text of block </div>; }
В данном примере, в моём понимании, "представление" это только следующая часть:
<div className='block'> Text of block </div>
А вся остальная обёртка – это, грубо говоря, логика (т.к. там может быть любой js код).
Но это лишь моё видение, не более того.
jMas
09.12.2016 18:04+3PHP template:
<?php foreach ($items as $item): ?> <a href="<?= $item->url; ?>"><?= $item->name; ?></a> <?php endforeach; ?>
Браузер в таком случа выполняет роль обработчика вашего шаблона, и браузеру вы делегируете переход по ссылке.
В React-приложении:
render() { const items = this.props.items; return items.map((item) => <button type="button" onClick={ this._handleClick }>{ item.name }</button>); }
вы сами занимаетесь обработкой логики нажатия на кнопку, браузер лишь показывает вам результат который apply-ит React.
React полезен для SPA, потому что позволяет проще контролировать изменение контента, дает больше контроля над тем, что происходит в вашем приложением, дулает обновления DOM практически безболезненными. Но это не значит что его нужно пихать везде.
Вы сами занимаетесь разделением: где хранить данные, где хранить обработчики. React просто дает возможность писать сложные представления, у которых много состояний проще.
norlin
09.12.2016 20:02<button type="button" onClick={ this._handleClick }>
вы сами занимаетесь обработкой логики нажатия на кнопкуВот это и есть смешение логики и представления. Что делать, например, если мне на кнопку надо несколько обработчиков повесить?
faiwer
09.12.2016 21:49-1Вот это и есть смешение логики и представления
Времена меняются. Теперь это норма. Во всяком случае для SPA.
Что делать, например, если мне на кнопку надо несколько обработчиков повесить?
Вы знаете, но с React и другими реактивными системами, необходимость сразу нескольких обработчиков на один элемент и одно событие ? событие исключительной редкости. Природа сего явления та же, что и у отсутствия необходимости $.fn.live. С таким подходом этого просто не нужно делать. Области влияния, видимости и прочего разделены так, что нет необходимости в этом.
auine
09.12.2016 22:17-5Что делать, например, если мне на кнопку надо несколько обработчиков повесить?
Иди прочитай книжечку по js и не пиши ересь
Если тебе надо там несколько обработчиков на клик, тебе никто не мешает их вызвать на onClick
mrLk
09.12.2016 22:50А в чем здесь смешение логики и представления? Как мне кажется это наоборот, позволяет отделить мух от котлет. Фактически работа представления заключается в предоставлении интерфейса для отображения данных и оповещение контроллера о событиях, в то время контроллер совершенно не обязательно знать внутрянку вью — простые хтмл-инпуты там, какие-либо богатые компоненты или просто заглушка в тестах. То есть this._handleClick теоретически может подготовить данные при необходимости, или сразу оповестить контроллер о неком событии. Если по простому, то в контроллере это может выглядеть примерно так:
doAddUser(fData) { /* validate data */ /* other work */ } let tForm = new EditUserView(tUserData, this.doAddUser)
При этом контроллеру совершенно всё равно, он не должен быть в курсе того, какие элементы используются, Button это или кнопки вообще нет, и событие срабатывает по нажатию на клавишу ввода — это не работа контроллера. И вью, при этом, не содержит никакой другой логики, кроме логики представления.jMas
10.12.2016 03:38Контроллер в SPA — это Action Creators + Flux / Redux сторы — они ответственны за создание и мутацию состояния приложения, React-компоненты просто (в идеале) должны заниматься репрезентацией того что вернул стор. Вот банальное разделение логики, данных и отображеня.
VolCh
10.12.2016 03:47Где, по-вашему, должна быть логика отображения?
jMas
10.12.2016 04:10Что вы имеете ввиду под "логикой отображения" — реакцию на "Button Click"?
Клик по Button создает Action, который попадает в стор, мутирует его состояние, состояние из стора попадает в React компонент, применяется новое состояние. Судя по всему, логика реакции на "Button Click" находится в сторе.
Но конечно же, какая то мелкая логика будет присутствовать в React-компонентах.mrLk
12.12.2016 09:44Имменно про обработчик клика мы и говорим, см. комментарий @norlin. this._handleClick в данном случае и должен/может подготовить данные/запустить событие. Аналогично можно рассмотреть простую html-форму, обычно ее работа принимается как должное, но если присмотреться, там есть и выбор файлов для <input type='file' .../> и подготовка данных, с погрузкой их в хттп-запрос и т.д. Так или иначе это остается логикой представления и свои аналоги this._handleClick там присутствуют.
js605451
10.12.2016 02:37+2Что делать, например, если мне на кнопку надо несколько обработчиков повесить?
Написать _handleClick таким образом, чтобы он дёргал несколько обработчиков. Вы вопрос неправильно ставите. Обработчик всегда один: нажатие кнопки означает, что юзер "даёт команду" вашему аппликейшну. Как вы там будете эту команду обрабатывать — одним хендлером или десятком — ваше дело. Кнопка со своей задачей справилась — донесла до вас что её нажали.
GIum
09.12.2016 17:03-8Всю мою сознательную дейятельность JS-разработчика вокруг твердили «нехорошо смешивать логику и представление»
А зачем Вы себе взяли это утверждение за догму? Разработчики из facebook обосновали почему такой подход в сегодняшних реалиях не всегда работает.norlin
09.12.2016 17:26+2Потому что для меня это выглядит логично и удобно. Например, самым удобным шаблонизатором, из тех, которые я использовал, считаю DustJS именно из-за невозможности писать там логику, не относящуюся к отображению.
auine
09.12.2016 22:19-6Логично и удобно ложить в разные файлы декларатив и императив? Боже ну ложи если тебе так удобней. Но я не вижу вообще никакой необходимости в этом, так как все четко и в одном файле компонента. Композируй их как хочешь. Как это вообще может быть удобно? jquery бой
gearbox
09.12.2016 17:06+7>нехорошо смешивать логику и представление
Нехорошо смешивать БИЗНЕС логику и представление. Логику вью и само вью смешивать можно и нужно.norlin
09.12.2016 17:21-4В том-то и дело, что в представлении не должно быть никакой логики, помимо отображения данных. А вот какие данные и где отображать – это должно решаться в коде. Но это лишь моё мнение и я не хочу устраивать холивар на тему.
faiwer
09.12.2016 21:53+3Не представляю себе как можно организовать отображение достаточно сложного шаблона вообще без логики. Если вырезать из шаблонизатора все логические вветления, то вы просто будете вынуждены эту часть логики отображения данных выносить из шаблона на уровень выше. И, вероятно, это будет то же место, где у вас и модель лежит. Ерунда же получается. Чего вы этим добились, кроме как испортили себе модель?
Скорее в шаблонизаторах вредна возможность применения неодносложных выражений. Идеальный шаблонизатор полностью декларативен и достаточно прямолинеен.
norlin
09.12.2016 22:39-5В качестве примера, посмотрите на шаблонизатор DustJS, например. Там есть некоторая логика (if, циклы), но она используется исключительно для отображения ("есть данные – показываем одно, нет данных – другое").
vintage
09.12.2016 20:00+2Также не стоит смешивать императивное и декларативное описание, а не только доменную и презентационную логику. Если смешивать, то теряются все преимущества декларативного описания. Например, возможность применять различные логики к одному и тому же описанию. Например, из одного и того же декларативного описания можно получить как минимум:
- Код для отображения данных.
- Файл локализации.
- Информацию о структуре для визуального конструктора.
- Список и типы параметров.
- Список зависимостей.
js605451
10.12.2016 02:34+1Всю мою сознательную дейятельность JS-разработчика вокруг твердили "нехорошо смешивать логику и представление"
Вы путаете "логику" и "бизнес-логику". Спрятать кнопку или показать в зависимости от аутентифицированности пользователя — это как раз "логика представления". Будет это if внутри jsx, или это будет непонятная закорючка в handlebars — вообще никакой разницы.
- Вы путаете "представление" и "мой любимый шаблонизатор". Представление — это часть кода, которая "оформляет" ответ прежде чем ответ уйдёт к тому, кто прислал запрос. Код сериализации объекта в JSON — это такое же представление, как и натягивание модели на шаблон с целью получить HTML. Даже больше — когда вы пишете код, который через HTTP возвращает 200 — это тоже представление.
ThisMan
09.12.2016 17:12+4С нынешней схемой развития js, всегда будет необходимость в babel-e (других транспиляторах).Каждый раз всегда будет esX, которым хотят пользоваться, но которые еще не поддерживается в браузерах. К тому же, думаю и babel не такие дураки. уже сейчас есть babel-preset-env, где можно указать версию браузера, для которого билдится код и так уменьшить количество изменений.
faiwer
09.12.2016 21:56Лично я, искренне надеюсь, что разработчики браузеров пойдут на встречу
хипстерамweb-программистам и позволят, хотя бы с каким-нибудь флагом при запуске браузера, примешивать что-то своё в runtime-код загрузки скриптов. Тогда эти трансформации можно будет делать на лету, да ещё и на каком-нибудь быстром c++. Это сильно бы упростило работу :)
Frozik
09.12.2016 17:20+4Честно говоря не вижу профита, одни минусы:
- Дополнительная зависимость
- Дополнительное потребление памяти, под кеширование + процессорное время на компиляцию
- Сокрытие кода JSX от линтеров
- Вопрос с поддержкой Typescript / новых фич JS
- Ну и вопрос с отладкой
dyakov
09.12.2016 17:51+1Дополнительная зависимость
Пакет имеет только одну зависимость — на пакет для парсинга, который не имеет зависимостей. В сумме около 500 строк кода, в сжатом виде около 2кб
Дополнительное потребление памяти, под кеширование
Не очень много, какие-то несколько мегабайт на тысячу шаблонов. Сравните с гигабайтами, которые потребляет запущенный вебпак.
Сокрытие кода JSX от линтеров
Вопрос времени — появятся плагины к линтерам для парсинга кода в шаблонных строках.
Ну и вопрос с отладкой
Парсер не молчит об ошибках а кинет exception. Вы знаете удобный способ отлаживать выход jsx? Или вы про ошибки во время транспиляции?
EugeneGavriloff
10.12.2016 14:05Прочел статью, посмотрел поддержку ES6 на ПК и мобильными устройствами. На мобилках всё печально до сип пор, плюс ко всему, лично я уже пользуюсь ES Next, поэтому отказаться от транспиляции, похоже никогда не представится возможности xD
Akuma
Так и не понял, зачем отказываться от JSX?
Просто, удобно, «стандартно». Всякими бабелями/вебпаками все равно пользуемся.
dyakov
Пока пользуетесь, да. Вам все равно.
Я в ближайшем будущем планирую все сделать, чтобы избавиться от их необходимости в develop-окружении. Это даст плюсы — 1) в дебаггере виден неизмененный сборщиком код, номер строк будет совпадать, 2) не надо будет ждать пересборки на любое изменение (в большом проекте с вебпаком это может занимать пол минуты и больше)
Ну и jsx не входит в язык javascript, вряд ли войдет. Стандартно (а теггированные строки — часть стандарта) все же правильнее, имхо. Особенно, если остаются все плюшки и необходимы минимальные изменения по сравнению с jsx.
Кстати, для скорости в проде, возможен плагин к babel, который на выходе будет давать все тоже что сейчас дает jsx.
Akuma
Хм. Как вы например без дев-окружения будете вырезать из кода отладку?
Вообще, идея хорошая, не спорю. Чем меньше «шагов» к сборке, тем быстрее и проще. Но на текущий момент вебпак и бабель позволяют делать то, что без них я не могу. А JSX банально удобнее, чем ваше (и многие другие) предложения.
vintage
А зачем её вырезать? Очень удобно прямо на проде, включить логи и посмотреть что там идёт не так.
justboris
vintage
faiwer
Да нет, не капля. Некоторые библиотеки добавляют довольно много debug-кода. К примеру knockout, react. На одной из связок недавно проверял сколько будет, вышло в р-не 10-15% выигрыша.
vintage
Что же они такого туда добавляют?
faiwer
Не ко мне вопрос. Наверное инструменты для отладки, примешивают dev-поля к своим объектам, ну и т.д… Могут даже альтернативные алгоритмы подключать, такие что позволят легче дебажить к примеру. Вы об этом лучше их самих спросите.
vintage
Тут скорее повод не пользоваться такими странными инструментами, чем заниматься отладкой не того кода, что будет исполняться у пользователя.
faiwer
При желании можно подключать не-debug версии скриптов и в dev-е. В чём проблема? Или вы замечаете "фатальные недостатки" уже во всём? :) Даёшь всем $Mol?
demiazz
Аргумент в принципе логичен с первого взгляда, но упускает множество моментов.
Во-певрых, отдельно придумывать архитектуру внутри библиотеки, так, чтобы подключать туда расширения только для отладки — это не всегда лучшее или удачное решение. Или лучше сказать — это не всегда наилучший и единственный путь. В конце концов, вы же не рассказываете условным C++-разработчикам, что сборка бинарных файлов с отладочной информацией — это плохо, и поэтому C++ — это странный инструмент, правильно?
Во-вторых, тот же React в development режиме вырезает не только отладочную информацию, но и например, вырезает проверку propTypes. А еще при сборке, их можно вообще вырезать из кода тем же плагином.
В-третьих, вы упускаете момент, что например, есть, например, минификатор babili, который построен поверх babel. Babel и подобные инструменты — это не только полифилл для новых версий языка.
В-четвертых, ECMAScript в отличии от ранних версий, сейчас активно развивается, и говорить о том, что сейчас все фичи будут во всех мажорных браузерах, и нам не нужен будет transpiler — преждевременно. Transpiler используется для проверки и реализации новых возможностей языка (в том числе, посмотрите на процесс TC-39). Во-вторых, новые возможности можно использовать уже с Stage 3 (когда допускаются только критические исправления). Но есть еще Stage 4. И это только до момента внедрения в браузеры (этот процесс тоже не равномерен по времени). То есть, transpiller позволяет подготовить ваш код к будущему переходу на нативную поддержку в движках языка до того момента, как эта поддержка настанет.
В-пятых, отсылка на отказ от source map также преждевременна. Вы можете отказаться от babel, но вы же минифицируете код? Значит, уже нужны source map.
В-шестых, отладка в production… Ну как бы это сказать. Этот этап должен предварять этап сбора отладочных сведений путем таких инструментов как Sentry, TrackJS, Honeybadger, и других. И потом уже можете воспроизводить неисправность локально, зная условия, которые приводят к ошибке. В 99.99% случаев, проблема будет не в сборке.
Transpiler'ы — это данность, поэтому и отказ от JSX, только потому что он требует transpiler'а, который скоро будет не нужен — это слабый, на самом деле, аргумент.
Я уже не говорю о том, что, экономить килобайты важно до сих пор. По очень многим причинам. Странно это вообще объяснять фронтенд-разработчику, не находите?
demiazz
Кстати, еще пока вспомнил, и касательно конкретно React. В dev и production режиме, JSX транспайлится по разному. createElement против непосредственно JS объектов. Первый дает больше возможностей для отладки, второй — быстрее работает.
Что касается разбора шаблонов непосредственно на клиенте — мы хотим дать лучший UX. Если мы можем отказаться от дополнительных шагов, и быстрее показать страницу пользователю — почему это не делать? Зачем дополнительный шаг? Есть ведь мобильные, есть простые пользователи с не совсем производительными устройствами.
На секундочку, Angular 2, компилирует шаблоны в императивный JS-код непосредственно.
Или вот https://svelte.technology/ от Рича Харриса (автора Rollup). Фреймворк компилирует компоненты в непосредственно императивный код.
Технологии двигаются в сторону оптимизации, тут же предлагается компилировать шаблоны на клиенте (даже во времена классических строковых шаблонизаторов, хорошей практикой была прекомпиляция шаблонов Jade/Pug/etc в функции).
Я уже не говорю о том, что современные редакторы умеют в подсветку JSX синтаксиса, и его легче читать, чем HTML шаблон в строке со вставками.
Еще один момент — компиляция JSX шаблона быстрее покажет ошибку в шаблоне (просто банально не сможет распарсить структуру), нежели мы получим ошибку в runtime при попытке отрендерить страницу. В конце концов, редакторы, вполне себе уже поддерживают JSX и укажут на несоответствие синтаксиса. Это конечно, по большому счету лирика, но тем не менее.
vintage
Это какие такие возможности?
Странно слышать это от человека, генерирующего html яваскриптом :-D
Это не быстрый процесс. И результат достаточно сложно отлаживать.
Великое достижение. Вместо того, чтобы собирать в бандл только то, что реально используется, в него подключается вообще всё, а потом вырезается то, что не используется.
Попробуйте допустить в коде хоть одну ошибку — концов вы не найдёте.
Ага, HTML внутри JS читать куда легче, чем JS внутри HTML :-) Я вот как-то особой разницы не вижу — и там и там каша получается из разных синтаксисов.
Если вы считаете это полезным, то почему используете JSX, а не TSX?
vintage
Рассказываю. Это очень удобно, когда отладочная информация лежит в отдельном файле.
propType — это рантайм эмуляция статической типизации. Что мешает использовать полноценную статическую типизацию в виде TSX, которую не нужно "вырезать"?
Есть много разных минификаторов. Сейчас они используются скорее по инерции, так как на фоне gzip практически ничего не дают.
А я этого и не говорил. Яваскрипту давно уже надо было перейти на байткод :-)
А это вы откуда взяли? У меня сорсмапы кладутся в отдельный файл. А вы вкомпиливаете их прямо в код? Зачем?
Хорошо там у вас наверно в идеальном мире? :-)
Правильная архитектура позволяет экономить гораздо больше.
demiazz
Круто, расскажите как отладочную информацию положить в отдельный файл в случае обсуждаемых выше задач? Кроме source maps.
А я не отметал возможность использования TSX, только вот, TypeScript не всем нужен и не всем нравится. Так то и FlowType есть. Но как и в случае с TS — не все используют.
Ну не знаю. У меня почему-то, даже zopfli дает ощутимо разные результаты после и без минификации. Вы делаете слишком обобщенный вывод.
А с чего вы взяли, что я имею ввиду только вариант использования source maps внутри скомпилированного файла, а не как отдельного? =) Возможно, я зацепил информацию из треда ниже или выше, не напрямую к вам относящуюся.
Идеальный мир — делаем мы сами. TrackJS умеет в телеметрию, например. Так что, он не такой идеальный, а скорее реальный. Вы же используете, например, console.log. Ну так же вместо него можно сообщения отправлять в трекер. Лучше, чем искать ошибку на продакшене, не так ли? =)
И как это связано с размером результирующего бандла приложения? Ну можно и System.import вспомнить, и асинхронную догрузку кусков приложения. И многих подходов. $mol тут чем особо выделяется? Тем что минималистичен?
vintage
Расскажите, что это за информация.
В таком случае разглагольствования про выявление ошибок на этапе сборки — ни о чём.
Он передаёт на сервер слепок памяти? Или как обычно — лишь создаёт видимость в духе "ну браузер такой-то, ось такая-то"?
Ваш трекер не умеет перехватывать console.log?
Лучше искать там, где воспроизводится. Вы соскочили с темы "как понять причину ошибки" на тему "как воспроизвести неуловимую ошибку".
Да не стоит.
Микромодульностью.
demiazz
Да банальная отладочная информация. Например, отладочные сообщения, ворнинги. Которые имеют смысл мне локально, но не имеют смысла в продакшене для пользователя. Мы же с этого начинали. Или опять таки propTypes, или те же типы (они бесполезны в рантайме в случае JS, увы). Мы кажется с этого начали — зачем пихать отладочную информацию, чтобы потом удалять её сборкой, разве нет?
Какая-то странная ультимативность. Type checker выявит ошибки типов (большинство, не все — он в рантайме не защитит от кривых входных данных — ошибка отловится, но уже где-то глубже, а не на точке входа). Но типы не поймают ошибку, если в es6x будет неправильный шаблон, и сборка не поймает. Так что разглагольствовать есть о чем. Кроме того, есть trade off между TS/Flow, и выразительностью, например https://twitter.com/dan_abramov/status/808020948750397441
Вы отлаживаете клиентский JS по слепку памяти? =) Нет, конечно, речь идет не о банальном environment'е, что трекеры умели в бородатые годы. Речь идет о формировании лога событий, предшествующих ошибке https://trackjs.com/assets/images/tour/telemetry-full.png Я могу ошибаться (давно не пользовался), но кажется в sentry подобная штука называется Breadcrumbs.
Правильно. Если она воспроизводится локально — то лучше её там и искать. Трогать продакшн конечно же нужно, но если можно этого избежать — почему не воспользоваться этим? =)
Честно, пробежался по репозиторию, и магической микромодульности не увидел. Просто свой набор утилит + компоненты, только самописные и интегрированные между друг другом, а не с npm. Крутое решение для класса задач, но я не вижу как им заменить React, и кастомизируемую сборку. И да, я люблю конечно, convention over configuration, но так же я люблю явные зависимости. Если бы, у вас был dependency injection, аля Angular — это было бы интереснее, но у вас просто парсинг своих соглашений в поисках зависимостей, вместо import/export из es6. Ну как бы, это не микромодульность, ну или не чем-то выделяющаяся, извините.
Что касается $mol вообще. В Питере целых два сообщества проводят митапы по фронтенд разработке. Почему бы вам не рассказать о нем на митапе? Не все пишут на React, в общем-то. Вполне вероятно вы найдете идею/критику/пользователей на них, рассказав о своем инструменте.
vintage
То есть этот инструмент настолько не пойми как работает, что для отладки кода с его использованием нужны десятки килобайт отладочных сообщений?
Типы несут полезную нагрузку. Неиспользуемые модули — бесполезную. При этом типы вырезаются при любой сборке, а не требуют для продакшена делать отдельную.
Речь шла про TSX, он поймает.
Чудесный костыль. Чуть более сложные внутренние контролы и это неявное соглашение ломается. Какое он имеет отношение к типизации?
Пользователь ввёл абракадабру, сделал 100500 действий, потом поскроллил пальцем и всё сломалось. Чем вам помогут последние действия пользователя?
Вы зашли на продакшен, видите багу. Что к ней привело — не понятно. Ваши действия?
А что вы ожидали увидеть? Что вы вкладываете в понятие микромодульности, помимо отсутствия монолитного ядра?
Реакт заменяется модулем $mol_viewer.
Что вы хотите там кастомизировать?
Там вполне явные зависимости. Когда вы пишете $mol_defer в бандл включается модуль $mol_defer. Куда уж явнее? То, что вы имеете ввиду — не явные зависимости, а зависимости вынесенные в начало файла. Объявления переменных вы тоже в начало файла выносите?
А вот это как раз неявные зависимости, ибо в месте использования никогда не знаешь какой модуль будет задействован. Навигация по коду ломается на пустом месте. Плюс код обрастает кучей одноразовых интерфейсов.
Потому же почему и тут карму сливают — я не Дэн Абрамов, $mol — не React, а работаю я не в Google.
demiazz
То есть, сообщения об использовании deprecated классов/модулей/функций, различные warnings, например — это признаки плохого инструмента? Выбирая из 20Мб отладочных сообщений, и модулей ля упрощения разработки в development, и отсутствие оного, зато не требующим шага при сборке — я выберу первый. Потому что, мне это ничего не стоит. Зато это сэкономит мне время, когда палка выстрелит.
Да. Я понимаю вашу позицию. Типы вырезаются при любой сборке, а вот сделать dead code elimination, или tree shaking — это уже в самописной build системе не получится сделать за пять минут и на коленке. Поэтому это сложно, а значит не нужно. Лучше делать отладку на production с пользовательскими данными, верить в то, что используемый инструмент идеально написан, и отладочная информация для слабаков, а настройка сборки — не нужна, потому что наш инструмент ее не умеет и вообще время, и вообще сложно.
Но речь как раз шла не об ошибках типов, на что вы сказали, что только типизируемый язык такие ошибки словит (хотя при чем тут типы). JSX тоже их поймает. Кажется мы тут упустили нить, но уже не хочу разбираться кто и когда ее упустил.
Это не костыль, это всего лишь синтаксис языка. Конечно сломается, но когда оно сломается — тогда и код перепишется. Да и тесты помогут. К типизации это относится ровно так, что TS/Flow не идеальны, и к тому, что краткость синтаксиса, не всегда доступна вместе с типизацией, и нужно выбирать.
Кстати, насчет TS. Как думаете, вот этот код TS поймет как неправильный:
function filterArray(a: any[]): number[] { return a.filter(i => typeof i === 'string'); }
? Можете в Playground даже попробовать. Надеюсь, банальныйfilter
вы костылями не назовете?Представьте себе, лучше большое количество бесполезных логов, среди которых есть действия, которые могли привести к ошибке. Такие логи — дополнительный ключ к пониманию, почему произошла ошибка и где ее искать.
Так это вам непонятно, что к ней привело. Я то как раз использую "ненужные модули" и "логи про то, как пользователь поскроллил", и могу довольно быстро локализовать ошибку. У меня на руках есть исключение, привязанное к source maps (если это было исключение), у меня есть примерный сценарий поведения пользователя (потому что пользователь никогда вам не скажет, что он сделал, что привело к ошибке), и у меня есть локальная система, где я могу попытаться воспроизвести ошибку, и задействовать бесполезные модули, которые нужно вырезать на этапе сборки. Так что, первым моим действием, будет анализ данных, а не гадание на production, как воспроизвести, и почему так получилось.
Я ожидал увидеть нечто из ряда вон. Я ожидал, какой-то магической "микро"-модульности. А на деле, простой компонентный подход, разделение на модули (откуда тут взялось микро — не понимаю, отсылка к модным ныне микросервисам?), который у многих уже давно.
Я имел ввиду не в рамках $mol, а как инструмент для решения класса задач.
Например, я хочу кастомизировать используемые PostCSS плагины (а кто-то использует вообще Less/Sass/Stylus — и это их право). И знаете. Не просто кастомизировать. У меня в рамках приложения, может быть допустим, несколько разных типов страниц — основные разделы для пользователя, пара SPA, админка, и пара лендингов. И для каждого я хочу свой набор PostCSS плагинов, потому что у них могут быть разные требования, или нужны по разному настроенные плагины. Или, я хочу импортировать CSS в JS, например. Я хочу, чтобы моя сборка считала зависимостью не только JS/CSS файлы, но и статику, которые те используют. И желательно, это дело тоже как-то оптимизировать. Я хочу прокидывать флаги NODE_ENV, например, но мы это проходили, да. Я хочу контроллировать, как будут собираться бандлы и иметь возможность оптимизировать стратегию их сборки и компоновки под нужды приложения. Я хочу, чтобы сборщик умел кешировать и умел в инкрементальную сборку в development, чтобы было быстро, и может быть умел hot reload. Хотел бы иметь сервер, который бы отдавал ассеты в development режиме, без нужды сбрасывать их каждый раз на диск, а отдавать прямо из памяти. Чтобы было быстро.
Ну это так себе — как явные зависимости. Если допустим, я напишу $mol_defer, но такой модуль не будет существовать — ваша сборка скажет мне об этом? А если, не будет существовать не JS файл, а какая-то другая зависимость — я об этом узнаю? Dependency Injection — механизм не явных зависимостей конечно. Но у него есть свои benefits, о которых много копий сломано, и рассказывать то, что можно прочитать в десятке мест — я не хочу. Кстати, этот же механизм есть и у Ember. И он тоже отчасти на соглашениях построен.
Ну может карму сливают не потому, что вы не Абрамов (и он не всегда в FB работал), а потому что, вы можете быть не правы? Или ваше мнение не совпадает с большинством? Роману Дворнову, вот ничего не мешает рассказывать про basis.js, при этом не имея огромной аудитории как у React.
vintage
Такое обилие deprecated говорит о плохом дизайне. Стал ли он лучше — большой вопрос. В любом случае сообщения эти обычно вставляются в одно единственное место определения, а не в каждое место использования. Так что расти объёму сборки тут особо не с чего.
А я вот выберу встроенный в браузер инструментарий. А не работающий только с одним единственным фреймворком, только на локальной машинке.
Мёртвый код надо не при сборке удалять, а ещё до коммита. TS, например, ругается на мёртвый код.
Этот костыль не нужен для микромодульной архитектуры. Ну вот совсем.
Речь шла про синтаксические ошибки.
Хрупкий код, который неизбежно ломается и на дебаг которого уходит куча времени — это и есть костыль.
Только выбирать приходится между временем отладки и "краткостью синтаксиса". Вы делаете выбор в пользу второго?
Пока нет, а что?
Такие логи обычно содержат слишком много мусора между действительно значимыми для воспроизведения событиями.
Чудесно. Я вам описал конкретную ситуацию. А вы пошли фантазировать про каких-то пользователей, какие-то исключения, какие-то попытки воспроизвести. Бага вот она, перед вами на проде. Должно было открыться одно окно, а открылось другое.
Редкий модуль превышает 3КБ исходников. И любой из них может не попасть в бандл, если не используется. Безо всяких роллапов и тому подобных костылей.
Что за "класс задач"?
Это зачем? Чтобы всех запутать?
Есть какая-то рациональная причина засовывать утку в зайца?
Зависимостью считаются все файлы в директории. Это одна из удобнейших особенностей MAM-архитектуры — оперирование не отдельными файлами, а целыми наборами файлов. Например, карта зависимостей "приветмира".
Поддержка этих форматов будет добавлена по мере появления в них потребности. И добавлена она будет сразу всем, а не только для одного разработчика, которому хочется "кастомизировать". И нет, нодовые модули для компиляции этих форматов всем ставиться не будут — только в те проекты, в которых есть файлы соответствующих типов.
Что конкретно вы хотели бы оптимизировать?
Он это уже умеет.
Приложения на $mol запускаются достаточно быстро, чтобы hot reload не требовался.
Сбросить пару файлов на диск — не такая уж и затратная операция.
Тайпскрипт ругнётся при сборке. IDE подчеркнёт красным. Но да, в общем случае приходится идти на компромисс между проверкой существования модулей и возможностью группировать/разгруппировывать модули не меняя код других модулей.
Dependency и прочие injection в $mol делаются простым переопределением свойств при создании объекта. Получается весьма удобно. Есть, правда, и минус — реализация по умолчанию тянется в любом случае. Но я не думаю, что требование делать по отдельному интерфейсу на каждый класс стоит полученной экономии.
Ага, так и есть. Но большинство ведь всегда право, да?
Ага, у себя на сайте. А на конференциях то про CSSO, то про удалённую отладку.
justboris
Сейчас проверил React 15.4.1
45.33 KB — в production-mode
63.25 KB — в development-mode
Разница на треть имеет значение.
Добавляют много разной иструментации и оберток, чтобы более явно указать на ошибку в пользовательском коде. Что конкретно — можно посмотреть в этом файле.
А еще warnings об использовании устаревших API, которые будут или уже удалены.
vintage
Подобный инструментарий, не относящийся к коду, лучше оформлять как расширение к браузеру, а не пихать к общей сборке, делая их вырезание необходимой операцией. Но даже если грузить вместе с кодом, то что мешает выносить эти дополнительные тулзы в отдельный бандл? Вместо того, чтобы что-то вырезать, можно это просто не добавлять.
Ну и необходимость в этом инструментарии — следствие кривой архитектуры, с которой плохо дружат стандартные отладчики.
Anarions
Реакт в дебаг-моде добавляет вещи которых нет в стандартных отладчиках — проверка типизации параметров в «шаблонах» (propTypes) — первое что приходит на ум, но вообще там много полезных инструментов.
vintage
С проверкой типизации куда лучше справляется TS и Flow, которые не нужно "вырезать при релизной сборке". Какие ещё там есть "полезные инструменты" и что они забыли в одном бандле с остальным кодом?
Anarions
TS справляется «слишком» хорошо, и не всем нравится типизация повсеместно. React решает этот вопрос более гибко. Они не в банде с основным кодом — они в дебаг-сборке. Собираете прод версию — и всё нормально. В компилируемых языках вас билд-конфиги тоже смущают?
justboris
Это еще не значит, что нужно упрощать им жизнь, выдавая больше отладочной иформации и показывать, куда копать.
vintage
Может вы ещё и код обфусцируете и вешаете намертво браузер при открытии отладчика?
bigslycat
Если очень нужно, то можно и не включать отладочный код в общий бандл, а подключать по запросу. А доступ к самому файлу можно ограничить на бэкенде, хоть на уровне приложения, если там такое предусмотрено, хоть как-либо ещё.
Shifty_Fox
Странно, на ноутбуке (i7 конечно, но мобильный и 2013 года), сборка сотен js файлов с babel занимает полсекунды. Номера строк у меня всегда совпадают, это же source maps, для этого они и нужны. Как то же люди работают на type script и coffee script. Как ни крути, браузер содержит в себе jit компилятор js, который конечно быстрее babel, но на мой взгляд логично заниматься импрувом производительности babel, а не пытаться избавляться от него. В конце концов, еще на development можно сделать ту же inline транспиляцию.
Alternator
Подскажите, пожалуйста что такое inline транспиляция.
А то гугл выдает ссылку на ваш же коммент.
Shifty_Fox
Я имел ввиду подключить в браузер babel, чтобы он компилировал js на лету.
faiwer
Не знаю как вы готовите sourceMap-ы и транспайлеры, но у меня они пока что вызывают только боль. Особенно в купе с regenerator-ом. Попросту я уже привык, что Chrome Dev Tools часто показывает погоду на Марсе, вместо реальной строки при отладке, не позволяет трассировать код без боли (например уходит в дебри какой-нибудь proxy-функции обёртки из babel-я), попросту падает иногда. А ошибки от JSX приходится отлаживать с console.log, т.к. стек трейс всегда показывает всё что угодно кроме полезной информации, и остаётся только сама ошибка. Отдельную боль доставляет постоянно тут и там вылезающие ссылки в стек трейсах и ошибках на не smap-версии файлов (т.е. настоящие). В случае webpack-а они ещё и невыносимо большие (чёрт ногу в этих бандлах сломает). Ух… Я стал просто повсюду вешать console.log, привык к постоянным тормозам (webpack часто не успевает пересобрать достаточно быстро даже маааленький проект с зависимостями), и почти мёртвому debug-у. Радуюсь только возможности использовать async/await и jsx.
В итоге склоняюсь к таким схемам:
Пока останавливает лень, меньшие возможности, и тот факт, что баги на production-е могут сильно отличаться от dev-версии.
faiwer
Хотя, с другой стороны, немного поработав со Scala, я понимаю, что ребята из мира Java готовы ждать и по 15 секунд на изменение и пересборку. Это у них норма. Это мы "скриптовики" разбалованы до-babel-ми временами. Но у java-разработчиков хоть stack-trace-ы не разъезжаются. А асинхронностью головного мозга они куда реже страдают, что упрощает отладку.
gearbox
>А ошибки от JSX приходится отлаживать с console.log, т.к. стек трейс всегда показывает всё что угодно кроме полезной информации, и остаётся только сама ошибка.


Вот я создал ошибку:
Вот так я провалился (в typescript код, обратите внимание):
webpack, typescript, tsx, все дела. не то что бы я настаивал, но как бы — а что не так?
Shifty_Fox
Вы знаете, да, мне пришлось повозиться с source maps. Я использую browserify, babelify, uglify. А, и exorcist для извлечения source maps из bundle в отдельный файл. И в общем-то вся проблема с этим была именно в путях — нужно повозиться, поиграть с опциями путей, basePath к исходниками, чтобы source maps корректно их цепляли. Я потратил на это где-то день, зато теперь просто копирую настройки из проекта в проект.
vintage
Код, сгенерированный из шаблонных строк всё-равно ведь не будет "совпадать".
faiwer
Ну дык и JSX-код как монолит, разве не так?
auine
лол что? sourcemap не слышал?
Какие проблемы? Это же не php с html :)
salex772
Иногда приходится обходится без JSX для транспайлинга других шаблонов в реакт, например PUG/Jade. Но рано или поздно все равно приходится в ручную их переписывать.