Кир Белевич

Кир Белевич

Темы
Неделя
Jul 20, 2015 → Jul 26, 2015

Архив недели @deepsweet

Понедельник


всем привет, эту неделю с вами будет @deepsweet. меня зовут Кир, кто-то может помнить меня по Яндексу, для остальных я просто JS-разработчик

т.к. у меня GMT+7 и вы скорей всего спите, я пока поспамлю темами, на которые мне было бы интересно поговорить :)

webpack, Babel, React, Flux, BEM (в хорошем смысле), изоморфность, фотки тупых людей, иммутабельность, парное программирование, Вьетнам.

на текущей работе есть всё из этого списка, очень красиво переплетённое. да-да, React + BEM :) с наследованием по уровням переопределения.

для всех – тема CSS Modules началась примерно отсюда github.com/webpack/css-lo…, и развивается дальше здесь github.com/css-modules

идея в чём-то гениальна: css-loader для webpack превращает классы из стилей в хэши, которые потом экспортируются как из обычного js-модуля.

дальше где-то, где эти стили нужны (в том же react-компоненте), они импортируются, и появляется доступ к маппингу "класс -> хэш".

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

в итоге получается такая себе автоматизированная связка BEM + <style scoped> без лишней боли по поводу конфликтов и пересечений.

кстати, как вам затея с эмоджи в коммитах? github.com/yummies/genera… 😼

собственно, основная идея React + BEM в том, что JSX – говно, а BEMJSON – добро github.com/yummies/yummie…

дальше – наследование github.com/yummies/core-c…, ещё дальше – "уровни переопределения" или, как мы их называем, "слои" github.com/yummies/babel-…

BEM-модификатор – это класс, который (автоматически) наследуется от класса react-компонента и до/переопределяет BEMJSON из его render()'а.

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

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

уже в черновиках статья. в целом – Flux может быть абсолютно любым. RT @RusAlexander: @jsunderhood на чью реализацию похож ваш #flux ?

завтра будет день Flux: иммутабельные деревья, единое состояние, курсоры, GraphQL-like декларативность, префетчинг на сервере, изоморфность.

довольно интересная концепция получилась с "темами" – тема просто является отдельным слоем, начиная с темы "reset" github.com/yummies/theme-…

пример корневого .yummies.yml из внутреннего проекта gist.github.com/deepsweet/15c8…

видимо эта неделя наконец-то заставит нас с @mistadikay сделать starter-kit.

магия заключается в спец. #-импорте: import Example from '#example'; import ExampleTypeTestSizeBig from '#example?_type=test&_size=big';

Babel-плагин матчится на символ # в импорте и начинает искать файлы по слоям из конфига. находит – включает в цепочку наследований.

в конечном итоге в Example попадает обычная фабрика реактового компонента, который отнаследован по очереди от класса каждого слоя.


пример модификатора github.com/yummies/core-c…super.render()

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

главное сильно и навсегда не залипнуть на "каноническом" Flux, это лишь начало :) завтра будут стримы, а послезавтра одно лишь телевидение.

быстро набросал кое-какой Starter Kit github.com/yummies/starte… 🔥

уже не представляю как можно жить без "Object Rest/Spread Properties" github.com/sebmarkbage/ec… – даже если не войдёт в ES7, буду юзать плагин

вообще, колонка Stage в ES7+ пока выглядит довольно печально github.com/tc39/ecma262

Create random JSON objects using json-spawn and Chance.js github.com/luisfarzati/js…

Awesome list of Redux examples and middlewares github.com/xgrommx/awesom…

совсем забыл, со мной можно говорить про Diablo 2.

Вторник


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

хорошее интро в Immutable Data на примере Immutable.js всё от того же фейсбука youtube.com/watch?v=I7IdS-…

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

это дерево иммутабельное, т.е. каждое изменение влечёт за собой новую версию дерева, отличную от старой.

в типичном приложении в дереве будет не так много основных явных веток, типа products list, product info и т.п.

"курсор" – это своеобразная ссылка на ветку данных в дереве. простой пример на основе всё того же Immutable.js github.com/facebook/immut…

"cursor path" – это путь к этой ветке или к конечным данным. обычно представляется в виде массива, [ 'products', ID, 'details', 'price' ].

для дерева и курсоров мы используем Baobab github.com/Yomguithereal/… – ничего лишнего, всё довольно просто и прозрачно.

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

событие update есть и у самих курсоров – изменились данные по конкретному пути в дереве.

сначала мы делали так: всё дерево помещается в локальный стэйт корневого компонента. по подписке на update стэйт обновляется новый деревом.

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

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

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

"лишние" рендеры довольно легко отсекались через shouldComponentUpdate(), как и было завещано.

но потом моё внимание привлекла очень интересная особенность Baobab: есть событие get – были запрошены данные по такому-то пути. геттер.

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

Declarative data fetching in React components with Baobab medium.com/@mistadikay/de…

компоненты – максимально тупые. они просто рисуют данные как будто эти есть и всегда были. почти каждый компонент сам знает свой cursor path

в рутовом компоненте идёт подписка на геттер из дерева – если данных, которые запрошены отрисовкой компонента ещё нет, то дёргается экшн…

…который, как в самом обычном Flux, диспатчит событие, которое ловит стор, который складывает эти данные по нужному cursor path в дерево.

компонент по событию update от своего курсора самообновляется и перерисовывается. profit.

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

самый жир затеи – github.com/mistadikay/rea… – декларативный "запрос" данных. что-то вроде того же GraphQL, только поверх обычного Rest.

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

ещё одна интересная особенность – в дереве есть две осовных ветки – data и ui. в data собственно данные, а в ui – визуальные состояния…

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

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

два поста в тему: Your First GraphQL Server medium.com/@clayallsopp/y… / Moving from REST to GraphQL medium.com/@frikille/movi…

на данном этапе меня смущает даже сам синтаксис graphQL Query, но это просто с непривычки. с самой идеей по-моему всё в порядке.

(речь о github.com/sindresorhus и его модулях типа github.com/sindresorhus/c…)

а какое Flux решение используете вы и какие у него основные минусы и сложности?

brace yourself, завтра изоморфность.

о да. жду адептов секты правильных слов :) RT @lapanoid: @jsunderhood универсальность ;)

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

ооок, в CSS есть outline-offset codepen.io/SitePoint/pen/…

минутка ванильной вёрстки закончилась.

какое название вашей должности/профессии наиболее полно отражает реальность и в целом вас устраивает?

моё "Senior Front-End Developer" уже явно вносит больше путаницы в головы менеджеров, чем что-либо проясняет.

Среда


A modular, progressive, and beautiful Markdown and HTML editor github.com/bevacqua/woofm…

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

готовых starter kit'ов и прочих стабов хватает, например github.com/RickWong/react… / github.com/webpack/react-… / github.com/DavidWells/iso…

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

в React для этого предусмотрены обычный DOM render() и "серверные" renderToString() / renderToStaticMarkup() facebook.github.io/react/docs/top…

к слову, в react@0.14 их даже разнесут по разным модулям facebook.github.io/react/blog/201…

обычно webpack'ом делают одновременно два билда с двумя наборами бандлов на выходе: серверный и клиентский.

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

на сервере это обычный request.url из Express / Koa (koajs.com, наш выбор) / whatever, на клиенте – HTML5 History API.

стандартом де-факто является react-router github.com/rackt/react-ro…

на сервере:

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

через renderToStaticMarkup() (чтобы не было лишних атрибутов data-reactid, "чистый" HTML) строится руками доктайп, <head> и <body>.

через renderToString() вытягивается строка HTML из компонента, который решил рендерить роутер, и dangerouslySetInnerHTML вставляется в body.

вся эта портянка отправляется обратно в Express в виде ответа на запрошенный URL. казалось бы всё, наш HTML jт сервера готов.

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

а render() у реакта синхронный, и сам по себе ждать каких-то там экшенов и сторов не может и не будет.

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

придумали маппинг соответствия роута и экшна(ов) – серверный роутер дёргает нужный экшн и ждёт ответа от стора, пока тот не заполнится.

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

Ajax with less suck - (and node.js HTTP client to match) visionmedia.github.com/superagent/ – чуть не забыл, "изоморфный ajax".

очень важно для осознания: componentDidMount() не происходит "на сервере", т.к. ничего никуда не маунтится.

поэтому подписки на обновление сторов или дёргание каких-нибудь экшнов в этом методе будут проигнорированы renderToString()'ом.

и ровно из-за этого же использовать условия с typeof window или, прости господи, process.env.BROWSER и пр. – плохо.

весь чистый client-side компонента должен быть описан исключительно в его componentDidMount().

итак. на сервере теперь для нужного роута дёргаются нужные данные и вытягивается HTML-строка из нужных компонентов.

когда на клиенте активируется наш собранный клиентский бандл, он (как я представляю) построит VDOM и начнёт матчить с HTML по data-reactid

т.к. никаких данных, которые были запрошены на сервере, у клиента нет, то React просто построит diff относительно пустых данных и всё сотрёт

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

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

мы не придумали ничего лучше (а затем нашли подтверждение правильности своей идеи в других Flux рещениях), чем передать данные в HTML.

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

т.е. при серверном рендеринге, куда-нибудь в <body>: <script id="_bootstrap" type="application/json">{ ololo: true }</script>

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

сложит их в стор и сделает самый обычный React.render(…). в этой точке всё дальнейшнее управление роутами уходит в клиентский react-router.

теперь строка HTML от сервера полностью идентична тому, что построит реакт на клиенте. нулевой diff, profit.

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

т.к. данных для другой страницы в "boostrap" нет, ведь сервер рендерил только текущую страницу. начинается обычный Flux data flow.

если очень кратко: самые начальные данные клиент берёт из HTML, а дальше всё как обычно, никаких ограничений.

грабли, на которые мы наткнулись практически сразу: неучтённые синглтоны и on-подписки вместо once на сервере.

результат: расшаренные сессии и сторы между разными клиентами :) следите за этим.

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

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

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

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

в серверный бандл так же можно не включать всё вендорное, чтобы мучать зря файл на пару мегабайт – webpack.github.io/docs/library-a…

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

если для чисто клиентского компонента не дёргается экшн в серверном роутере, то он просто отрисуется пустым.

на клиенте в своём componentDidMount() компонент посмотрит, что данных в сторе (от бутстрапа) нет, и сам дёрнет свой экшн. даже изящно.

а у кого сейчас какой pet project? :)

Четверг


появились подвижки в мёрдже isparta в istanbul github.com/douglasduteil/… – покрытие оригинального ES6 кода через sourcemaps от Babel.

к слову, лоадеры к вебпаку для обоих – github.com/deepsweet/ista… / github.com/deepsweet/ispa…

Approaches to testing React components reactkungfu.com/2015/07/approa… – а как вы тестируете реакт-приложения?

у нас (пока) jsdom github.com/tmpvar/jsdom + старая добрая mocha github.com/rstacruz/mocha… + TestUtils facebook.github.io/react/docs/tes…

Babylon is a JavaScript parser used in Babel github.com/babel/babel/tr…

кажется решились на babel-plugin-typecheck, по крайней мере простые вещи. ненавижу JSDoc. github.com/codemix/babel-… + flowtype.org/docs/quick-ref…

хочется как в Babel – github.com/babel/babel/bl… – комменты комментами, а типы через flow type annotations.

(TypeScript для бедных)

Turn flow type annotations into comments github.com/babel-plugins/…

пруф или не было :) RT @roman01la: @jsunderhood @xgrommx уже и с ES7

на сегодня так github.com/mistadikay/rea…, мы над этим работает. RT @roman01la: @jsunderhood У вас своя реализация query в компонентах?

хм, ок. тогда почему не TypeScript?


btw, скоро Baobab@v2 github.com/Yomguithereal/…

на сегодня это тянется в пропсах, но уже есть кое-какие намёки. RT @roman01la: @jsunderhood вот я про роут и id как раз и хотел сказать :)

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

а скиньте примеры своего кода на ES6 с JSDoc.

хохма последней недели – "самовозбуждающийся стор"

↓ и дальше по реплаям.

JSCity is an implementation of the Code City metaphor for visualizing source code github.com/aserg-ufmg/JSC… 😻

Пятница


тема лично для меня всё ещё актуальная, может есть что-то очевидное и крутое?

coveralls в read-only, ну круто status.coveralls.io – видимо с этим же связаны последние затупы с pending очередью /@toivonens

оформили наш подход к работе со стэйтом в реакте github.com/mistadikay/doob, по мотивам поста medium.com/@mistadikay/de… 🔥

@__fro @jsunderhood @mista_k я просто чувствую, что с большим количеством форм ты не работал и не понимаешь всех трудностей
горячий тред :) RT @__fro: @jsunderhood @iSnifer @mista_k twitter.com/isnifer/status… , не? )

совсем забыл про важную фишку иммутабельности – сохранение ссылок на объекты. простейший shouldComponentUpdate без shallow-equal костылей.

в том же Baobab два курсора на одни и те же данные будут равны через ===, как и результат их .get()а.

извините, у вас есть минутка поговорить о баобабе? RT @milk_is_my_life: @__fro @jsunderhood точно, не заметил. нафиг флакс, всем по баобабу!

A Virtual DOM based AngularJS view renderer designed to be used with immutable data structures github.com/teropa/angular…

Суббота


в ESLint@1.0.0-rc-3 наконец-то сделали reset по умолчанию eslint.org/blog/2015/07/e…, т.е. нужно заполнять конфиг с нуля вдумчиво и руками.

если вы по какой-то странной причине всё ещё используете JSHint, то самое время перейти с выходом ESLint@1.0.0.



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

спор математика и инженера, блин.

(по большому счёту и JSCS не нужен, codestyle-правил в ESLint по крайней мере для меня уже достаточно)

(с одной стороны, разделение обязанностей это хорошо, с другой – два разных инструмента это слишком)

btw, husky – "Prevents bad commit or push (git hooks, pre-commit, pre-push and all that stuff…)" github.com/typicode/husky

когда кто-то всерьёз говорит "ES2015" вместо "ES6" [gif] replygif.net/i/100.gif

перестал следить за Node.js + io.js с момента анонса "foundation". что там? мёрдж? роадмапы?

homebrew-rmtree – remove a formula and its unused dependencies github.com/beeftornado/ho…

Воскресенье


(мы даже сделали в Yummies безумный экстенд отдельных файлов с propTypes по слоям, но пока так ни разу не воспользовались)

был хороший issue в тему github.com/facebook/flow/… – propTypes на фоне нормальной типизации кажется костылём, хоть в нём и есть свои фишки.

(Flow, конечно, хорош, но он статический "типизатор". по поводу динамической типизации я всё ещё верю в штуки типа github.com/codemix/babel-…)


задумка простая: перед тем как удалять, превращать все flow types annotations в инлайновые ассерты. только в dev-режиме, конечно же.

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

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

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

(я разлогинюсь где-то в 19 по МСК, если осталось что обсудить или спросить – самое время)

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

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

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

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

чаще всего такие люди физически не способны доводить начатые проекты до конца, т.к. одно дело набросать прототип с однострочными while [...]

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

вспомните своих коллег, у каждого такой есть :)

Я КНОПОЧКУ ДО СИХ ПОР ВСТАВЛЯЮ, УЖЕ 30 ЛЕТ ЖИВУ.

а как вам такое – "преждевременный отказ от копипасты"

notion image

ну или если это действительно критично github.com/babel/babel/is…

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

чем умнее программист, тем тупее его функции
notion image

эту забавную неделю с вами был @deepsweet. надеюсь, что было интересно, спасибо всем за общение :) логи будут здесь jsunderhood.ru/deepsweet

часто говоря "мы" я имел в виду себя и @mistadikay – моего коллегу, напарника по парному программированию и просто хорошего трансгендера.

так и живём :)

ещё увидимся в каком-нибудь cljsunderhood. логаут.

Ссылки