SlideShare a Scribd company logo
Упрощаем «жизнь»
Лебедев Константин
Задача
Project/README
npm install
Не заыбвайте про
preinstall
—  Установка registry на локальное хранилище
postinstall
—   Добавление githook'ов
—  pre-commit: grunt dev
—  post-merge: npm install
—   jam install — обновление jam-пакетов
—  Структура вложенности блоков
—  Путь к файлу и строка (ссылка, открывает файл в IDE)
—  Примеры использования с текущими параметрами
—  «Глаз» — открыть GUI c блоком для просмотра
Feast GUI
Что произошло?
—  spa-meetup-2.html — шаблон блока
—  spa-meetup-2.scss — стили
—  spa-meetup-2.js — описание поведения
—  spa-meetup-2.spec.js — спецификация, на основе которой строятся
примеры использования
./node_modules/feast/bin/assist create-block 
--path=path/to/ui-blocks/ 
01.
02.
<div> </div> .spa-meetup-2 {
padding: 10px;
background: red;
}
import feast from "feast";
import template from "feast-tpl!./spa-meetup-2.html";
import styleSheet from "feast-css!./spa-meetup-2.css";
 
export default feast.Block.extend({
name: "spa-meetup-2",
template,
styleSheet
});
01. 01.
02.
03.
04.
01.
02.
03.
04.
05.
06.
07.
08.
09.
avito-spa-meetup-2.html
<div>
<div bem:elem="picture">
<b:avatar src="..."/>
</div>
<div bem:elem="details">
<div bem:elem="description"><!-- описание --></div>
<b:button remit:click="close" text="Done"/>
</div>
</div>
01.
02.
03.
04.
05.
06.
07.
08.
09.
Как это работает?
2012 / Fest
Fest
—  XML -> (NODEJS) -> JSFUNC_TPL -> STRING
—  Вывод ошибок (файл + строка)
—  Трансформация на сервере ~1ms
—  Запуск «Главной» на Fest
—  Интеграция в «Почту», запуск «Календаря»
—  Разработка внутреннего UI Toolkit (библиотека блоков)
—  Запуск новой версии «Почты» и «Облака» на базе Toolkit
Fest / Проблемы
Возвращает строку
Feast
—  XHTML (CLIENT) -> JSFUNC_TPL -> JSON -> CITOJS -> VDOM
—  GUI для просмотра и редактирования блоков (Live coding
HTML/CSS/JS)
—  Инспектор блоков проекта
—  Визуализация связей
Live coding
—  BrowserSync
—  Webpack
—  node-watch + socket.io
Server
"use strict";
const fs = require("fs");
const http = require("http");
const watch = require("node-watch");
const app = http.createServer((req, res) => {
res.writeHead(200, {"Content-Type": "html/text"});
res.end();
});
const socketIO = require("socket.io")(app);
app.listen(2016, () => {
watch("path/to", {recursive: true}, (file) => {
fs.readFile(file, (err, content) => {
const ext = file.split(".").pop();
socketIO.emit(`file-changed:${ext}`, {file, content});
});
});
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
Client
<script src="//cdnjs/socket.io"></script>
<script>
const socket = io();
socket.on("file-changed:html", ({file, content}) => {
// ...
});
</script>
01.
02.
03.
04.
05.
06.
07.
<div class="btn">
Click me!
</div>
XML Parser
{
name: "div",
attrs: {class: "btn"},
file: "path/to/button.html",
line: 1,
children: [{
name: "#text",
value: "Click me!",
file: "path/to/button.html",
line: 2
}]
01.
02.
03.
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
<fn:for data="attrs.items" as="key">...</fn:for>
"fn:for": {
scope: true,
required: ["data"],
expressions: ["data"],
prepare: (node, attrs) => ({
as: attrs.as || "$value",
key: attrs.key || "$index",
data: attrs.data
}),
toCode: () => [
"__each($data, @@.children, function (&as, &key) {", // начало
 
"});" // конец
];
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
Оновление шаблона
socket.on("file-changed:html", ({file, content}) => {
feast.Block.all.forEach(Block => {
if (file === Block.prototype.template.file) {
const template = feast.parse(content, file);
Block.setTemplate(template);
Block.getInstances().forEach(block => block.render());
}
});
01.
02.
03.
04.
05.
06.
07.
08.
CSS Modules
CSS Modules: PostCSS + React
import React from "react";
import styles from "./button.css";
 
export default class Button extends React.Component {
render() {
return (
<button className={styles.btn}>
<span className={styles.icon}>
<Icon name={this.props.icon}/>
</span>
<span className={styles.text}>{this.props.value}</span>
</button>
);
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
PostCSS + React + react-css-modules
import React from "react";
import CSSModules from "react-css-modules";
import styles from "./button.css";
 
class Button extends React.Component {
render () {
return <button styleName="btn">
<span styleName="icon">
<Icon name={this.props.icon}/>
</span>
<span styleName="text">{this.props.value}</span>
</button>;
}
}
 
export default CSSModules(Button, styles);
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
@decorator
import React from "react";
import CSSModules from "react-css-modules";
import styles from "./button.css";
 
@CSSModules(styles)
export default class Button extends React.Component {
render () {
return <button styleName="btn">
<span styleName="icon">
<Icon name={this.props.icon}/>
</span>
<span styleName="text">{this.props.value}</span>
</button>;
}
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
Angular2
@Component({
selector: "my-app",
template: `<div class="app">{{text}}</div>`,
styles: [`.app { ... }`] // .app[_ngcontent-mjn-1] { }
})
export class App {
// ...
}
01.
02.
03.
04.
05.
06.
07.
08.
Feast
import feast from "feast";
import template from "feast-tpl!./button.html";
import styleSheet from "feast-css!./button.css";
 
export default feast.Block.extend({
name: "button",
template,
styleSheet
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
CSS Module «на коленке»
function toCSSModule(file, cssText) {
const classes = {};
cssText = cssText.replace(R_CSS_SELECTOR, (_, name) => {
classes[name] = name + "-" + simpleHash(file + name);
return "." + classes[name];
});
return {file, cssText, classes};
}
01.
02.
03.
04.
05.
06.
07.
08.
CSS Module «на коленке»
function simpleHash(str) {
let idx = str.length;
let hash = UNIQ_SEED;
 
while (idx--) {
hash = (hash * 33) ^ str.charCodeAt(idx);
}
 
return (hash >>> 0).toString(36);
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
CSS Module «на коленке»
{
file: "path/to/button.css"
cssText: "...",
classes: {
"button": "button-v9m8ae"
}
}
01.
02.
03.
04.
05.
06.
07.
JS / Hot Reload
JS / Hot Reload
class Foo {
constructor(value) {
this.value = value;
}
log() {
console.log(`Foo: ${this.value}`, this instanceof Foo);
}
}
01.
02.
03.
04.
05.
06.
07.
08.
JS / Hot Reload
var foo = new Foo(123);
foo.log(); // "Foo: 123", true
 
replaceClass(Foo, class NewFoo {
constructor(value) {
this.value = value;
}
log() {
console.log(`NewFoo: ${this.value}`, this instanceof NewFoo);
}
});
 
foo.log(); // "NewFoo: 123", true
foo instanceof Foo; // true
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
JS / Hot Reload
function replaceClass(OldClass, NewClass) {
const newProto = NewClass.prototype;
OldClass.prototype.__proto__ = newProto;
 
// Static
Object.keys(NewClass).forEach(name => {
OldClass[name] = NewClass[name];
});
 
// Prototype
Object.getOwnPropertyNames(newProto).forEach(name => {
OldClass.prototype[name] = newProto[name];
});
}
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
12.
13.
14.
JS / Hot Reload / Проблема
replaceClass(Foo, class NewFoo {
/* ... */
});
 
foo.constructor === Foo; // false
01.
02.
03.
04.
05.
JS / Hot Reload / Проблема / Решение
—  Webpack — оборачивает создание класса в специальный фраппер
—  Использовать обвязку для создания классов, например
createClass('MyClassName', {...});
Live Coding
—  React — Webpack/React Hot loader/Redux
—  Angular2 — mgechev/angular2-hot-loader (экспериментально)
—  Так же Hot Relaod есть для Ember, Vue, RiotJS и т.п.
Что ещё?
Extentions / DevTools
lahmatiy/component-inspector
// app/packages/require.config.js
var packages = [{
name: "suggests",
location: "packages/suggests"
}, {
name: "push-manager",
location: "packages/push-manager"
}];
require.config({packages: packages});
Пакеты: npm/bower/jam
jam install suggests --save
jam install push-manager --save
// app/some-controller.js
import suggests from "suggests";
import PushManager from "push-manager";
 
// ...
01.
02.
01.
02.
03.
04.
05.
06.
07.
08.
09.
01.
02.
03.
04.
05.
logger
Istanbul
Не протестировано
↓
Заключение
—  Документируйте процессы
—  Ищите инструменты и создавайте их сами
—  Не описывайте «шаги», пишите утилиты, которые их выполнят
—  Старайтесь писать универсальные компоненты, неотягощенные
сложной логикой
The End
github.com/RubaXa
@ibnRubaXa

More Related Content

What's hot

Drupal 7 and History.js
Drupal 7 and History.jsDrupal 7 and History.js
Drupal 7 and History.js
Вадим Малай
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5Technopark
 
Эффективное программирование на NodeJS
Эффективное программирование на NodeJSЭффективное программирование на NodeJS
Эффективное программирование на NodeJS
Yura Bogdanov
 
Ice Php Framework Preview Release
Ice Php Framework Preview ReleaseIce Php Framework Preview Release
Ice Php Framework Preview Release
Denis Shestakov
 
Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиГлеб Тарасов
 
Работа с БД в Drupal 7
Работа с БД в Drupal 7Работа с БД в Drupal 7
Работа с БД в Drupal 7
Eugene Fidelin
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9Technopark
 
хранение данных
хранение данныххранение данных
хранение данных
Noveo
 
Web осень 2013 лекция 3
Web осень 2013 лекция 3Web осень 2013 лекция 3
Web осень 2013 лекция 3Technopark
 
Caching on highload Drupal site - Alexander Shumenko
Caching on highload Drupal site - Alexander ShumenkoCaching on highload Drupal site - Alexander Shumenko
Caching on highload Drupal site - Alexander Shumenko
DrupalCampDN
 
Произвольная смена дизайна системного скроллбара
Произвольная смена дизайна системного скроллбараПроизвольная смена дизайна системного скроллбара
Произвольная смена дизайна системного скроллбараDevDay
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Yandex
 
Web осень 2013 лекция 7
Web осень 2013 лекция 7Web осень 2013 лекция 7
Web осень 2013 лекция 7Technopark
 
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиГлеб Тарасов
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8Technopark
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьYandex
 
Web осень 2013 лекция 4
Web осень 2013 лекция 4Web осень 2013 лекция 4
Web осень 2013 лекция 4Technopark
 
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSГлеб Тарасов
 

What's hot (20)

Drupal 7 and History.js
Drupal 7 and History.jsDrupal 7 and History.js
Drupal 7 and History.js
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5
 
Эффективное программирование на NodeJS
Эффективное программирование на NodeJSЭффективное программирование на NodeJS
Эффективное программирование на NodeJS
 
Ice Php Framework Preview Release
Ice Php Framework Preview ReleaseIce Php Framework Preview Release
Ice Php Framework Preview Release
 
Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с данными
 
Работа с БД в Drupal 7
Работа с БД в Drupal 7Работа с БД в Drupal 7
Работа с БД в Drupal 7
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
 
хранение данных
хранение данныххранение данных
хранение данных
 
Web осень 2013 лекция 3
Web осень 2013 лекция 3Web осень 2013 лекция 3
Web осень 2013 лекция 3
 
Caching on highload Drupal site - Alexander Shumenko
Caching on highload Drupal site - Alexander ShumenkoCaching on highload Drupal site - Alexander Shumenko
Caching on highload Drupal site - Alexander Shumenko
 
Произвольная смена дизайна системного скроллбара
Произвольная смена дизайна системного скроллбараПроизвольная смена дизайна системного скроллбара
Произвольная смена дизайна системного скроллбара
 
MongoDB@addconf
MongoDB@addconfMongoDB@addconf
MongoDB@addconf
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
 
Web осень 2013 лекция 7
Web осень 2013 лекция 7Web осень 2013 лекция 7
Web осень 2013 лекция 7
 
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
 
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
 
Web осень 2013 лекция 4
Web осень 2013 лекция 4Web осень 2013 лекция 4
Web осень 2013 лекция 4
 
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
 

Similar to Avito / SPA Meetup 2

TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИСTARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС2ГИС Технологии
 
Jsfwdays 2013-2
Jsfwdays 2013-2Jsfwdays 2013-2
Jsfwdays 2013-2
Pavlo Iuriichuk
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4Technopark
 
Web весна 2013 лекция 10
Web весна 2013 лекция 10Web весна 2013 лекция 10
Web весна 2013 лекция 10Technopark
 
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиКурсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиГлеб Тарасов
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Vladimir Kochetkov
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машин
Roman Dvornov
 
Систематизация экспрешнов в IE
Систематизация экспрешнов в IEСистематизация экспрешнов в IE
Систематизация экспрешнов в IE
Roman Komarov
 
Шаблонизация
ШаблонизацияШаблонизация
Шаблонизация
DALEE digital agency
 
Взломать сайт на ASP.NET
Взломать сайт на ASP.NETВзломать сайт на ASP.NET
Взломать сайт на ASP.NET
Positive Hack Days
 
Web осень 2012 лекция 10
Web осень 2012 лекция 10Web осень 2012 лекция 10
Web осень 2012 лекция 10Technopark
 
I tmozg js_school
I tmozg js_schoolI tmozg js_school
I tmozg js_schoolITmozg
 
Introduction in Node.js (in russian)
Introduction in Node.js (in russian)Introduction in Node.js (in russian)
Introduction in Node.js (in russian)Mikhail Davydov
 
Javascript
JavascriptJavascript
Javascript
Vasya Petrov
 
Типичный стек технологий для использования с Node.js
Типичный стек технологий для использования с Node.jsТипичный стек технологий для использования с Node.js
Типичный стек технологий для использования с Node.jsSerge Shirokov
 
JSSDK: Начало
JSSDK: НачалоJSSDK: Начало
Разработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PIРазработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PI
Ilya Chesnokov
 
Node.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчикаNode.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчика
Alexei Smolyanov
 
Доставка данных в реальном времени.
Доставка данных в реальном времени. Доставка данных в реальном времени.
Доставка данных в реальном времени.
beshkenadze
 

Similar to Avito / SPA Meetup 2 (20)

TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИСTARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
 
Jsfwdays 2013-2
Jsfwdays 2013-2Jsfwdays 2013-2
Jsfwdays 2013-2
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4
 
Web весна 2013 лекция 10
Web весна 2013 лекция 10Web весна 2013 лекция 10
Web весна 2013 лекция 10
 
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиКурсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!
 
Jquery
JqueryJquery
Jquery
 
CSS глазами машин
CSS глазами машинCSS глазами машин
CSS глазами машин
 
Систематизация экспрешнов в IE
Систематизация экспрешнов в IEСистематизация экспрешнов в IE
Систематизация экспрешнов в IE
 
Шаблонизация
ШаблонизацияШаблонизация
Шаблонизация
 
Взломать сайт на ASP.NET
Взломать сайт на ASP.NETВзломать сайт на ASP.NET
Взломать сайт на ASP.NET
 
Web осень 2012 лекция 10
Web осень 2012 лекция 10Web осень 2012 лекция 10
Web осень 2012 лекция 10
 
I tmozg js_school
I tmozg js_schoolI tmozg js_school
I tmozg js_school
 
Introduction in Node.js (in russian)
Introduction in Node.js (in russian)Introduction in Node.js (in russian)
Introduction in Node.js (in russian)
 
Javascript
JavascriptJavascript
Javascript
 
Типичный стек технологий для использования с Node.js
Типичный стек технологий для использования с Node.jsТипичный стек технологий для использования с Node.js
Типичный стек технологий для использования с Node.js
 
JSSDK: Начало
JSSDK: НачалоJSSDK: Начало
JSSDK: Начало
 
Разработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PIРазработка на Perl под Raspberry PI
Разработка на Perl под Raspberry PI
 
Node.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчикаNode.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчика
 
Доставка данных в реальном времени.
Доставка данных в реальном времени. Доставка данных в реальном времени.
Доставка данных в реальном времени.
 

Avito / SPA Meetup 2

  • 3.
  • 4.
  • 5.
  • 6.
  • 8.
  • 9. npm install Не заыбвайте про preinstall —  Установка registry на локальное хранилище postinstall —   Добавление githook'ов —  pre-commit: grunt dev —  post-merge: npm install —   jam install — обновление jam-пакетов
  • 10.
  • 11.
  • 12.
  • 13.
  • 14. —  Структура вложенности блоков —  Путь к файлу и строка (ссылка, открывает файл в IDE) —  Примеры использования с текущими параметрами —  «Глаз» — открыть GUI c блоком для просмотра
  • 15.
  • 17.
  • 18.
  • 19.
  • 20. Что произошло? —  spa-meetup-2.html — шаблон блока —  spa-meetup-2.scss — стили —  spa-meetup-2.js — описание поведения —  spa-meetup-2.spec.js — спецификация, на основе которой строятся примеры использования ./node_modules/feast/bin/assist create-block --path=path/to/ui-blocks/ 01. 02.
  • 21. <div> </div> .spa-meetup-2 { padding: 10px; background: red; } import feast from "feast"; import template from "feast-tpl!./spa-meetup-2.html"; import styleSheet from "feast-css!./spa-meetup-2.css";   export default feast.Block.extend({ name: "spa-meetup-2", template, styleSheet }); 01. 01. 02. 03. 04. 01. 02. 03. 04. 05. 06. 07. 08. 09.
  • 22.
  • 23.
  • 24.
  • 25. avito-spa-meetup-2.html <div> <div bem:elem="picture"> <b:avatar src="..."/> </div> <div bem:elem="details"> <div bem:elem="description"><!-- описание --></div> <b:button remit:click="close" text="Done"/> </div> </div> 01. 02. 03. 04. 05. 06. 07. 08. 09.
  • 28. Fest —  XML -> (NODEJS) -> JSFUNC_TPL -> STRING —  Вывод ошибок (файл + строка) —  Трансформация на сервере ~1ms —  Запуск «Главной» на Fest —  Интеграция в «Почту», запуск «Календаря» —  Разработка внутреннего UI Toolkit (библиотека блоков) —  Запуск новой версии «Почты» и «Облака» на базе Toolkit
  • 30. Feast —  XHTML (CLIENT) -> JSFUNC_TPL -> JSON -> CITOJS -> VDOM —  GUI для просмотра и редактирования блоков (Live coding HTML/CSS/JS) —  Инспектор блоков проекта —  Визуализация связей
  • 32. Server "use strict"; const fs = require("fs"); const http = require("http"); const watch = require("node-watch"); const app = http.createServer((req, res) => { res.writeHead(200, {"Content-Type": "html/text"}); res.end(); }); const socketIO = require("socket.io")(app); app.listen(2016, () => { watch("path/to", {recursive: true}, (file) => { fs.readFile(file, (err, content) => { const ext = file.split(".").pop(); socketIO.emit(`file-changed:${ext}`, {file, content}); }); }); }); 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17.
  • 33. Client <script src="//cdnjs/socket.io"></script> <script> const socket = io(); socket.on("file-changed:html", ({file, content}) => { // ... }); </script> 01. 02. 03. 04. 05. 06. 07.
  • 34. <div class="btn"> Click me! </div> XML Parser { name: "div", attrs: {class: "btn"}, file: "path/to/button.html", line: 1, children: [{ name: "#text", value: "Click me!", file: "path/to/button.html", line: 2 }] 01. 02. 03. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11.
  • 35. <fn:for data="attrs.items" as="key">...</fn:for> "fn:for": { scope: true, required: ["data"], expressions: ["data"], prepare: (node, attrs) => ({ as: attrs.as || "$value", key: attrs.key || "$index", data: attrs.data }), toCode: () => [ "__each($data, @@.children, function (&as, &key) {", // начало   "});" // конец ]; } } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17.
  • 36. Оновление шаблона socket.on("file-changed:html", ({file, content}) => { feast.Block.all.forEach(Block => { if (file === Block.prototype.template.file) { const template = feast.parse(content, file); Block.setTemplate(template); Block.getInstances().forEach(block => block.render()); } }); 01. 02. 03. 04. 05. 06. 07. 08.
  • 38. CSS Modules: PostCSS + React import React from "react"; import styles from "./button.css";   export default class Button extends React.Component { render() { return ( <button className={styles.btn}> <span className={styles.icon}> <Icon name={this.props.icon}/> </span> <span className={styles.text}>{this.props.value}</span> </button> ); } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14.
  • 39. PostCSS + React + react-css-modules import React from "react"; import CSSModules from "react-css-modules"; import styles from "./button.css";   class Button extends React.Component { render () { return <button styleName="btn"> <span styleName="icon"> <Icon name={this.props.icon}/> </span> <span styleName="text">{this.props.value}</span> </button>; } }   export default CSSModules(Button, styles); 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16.
  • 40. @decorator import React from "react"; import CSSModules from "react-css-modules"; import styles from "./button.css";   @CSSModules(styles) export default class Button extends React.Component { render () { return <button styleName="btn"> <span styleName="icon"> <Icon name={this.props.icon}/> </span> <span styleName="text">{this.props.value}</span> </button>; } } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15.
  • 41. Angular2 @Component({ selector: "my-app", template: `<div class="app">{{text}}</div>`, styles: [`.app { ... }`] // .app[_ngcontent-mjn-1] { } }) export class App { // ... } 01. 02. 03. 04. 05. 06. 07. 08.
  • 42. Feast import feast from "feast"; import template from "feast-tpl!./button.html"; import styleSheet from "feast-css!./button.css";   export default feast.Block.extend({ name: "button", template, styleSheet }); 01. 02. 03. 04. 05. 06. 07. 08. 09.
  • 43. CSS Module «на коленке» function toCSSModule(file, cssText) { const classes = {}; cssText = cssText.replace(R_CSS_SELECTOR, (_, name) => { classes[name] = name + "-" + simpleHash(file + name); return "." + classes[name]; }); return {file, cssText, classes}; } 01. 02. 03. 04. 05. 06. 07. 08.
  • 44. CSS Module «на коленке» function simpleHash(str) { let idx = str.length; let hash = UNIQ_SEED;   while (idx--) { hash = (hash * 33) ^ str.charCodeAt(idx); }   return (hash >>> 0).toString(36); } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10.
  • 45. CSS Module «на коленке» { file: "path/to/button.css" cssText: "...", classes: { "button": "button-v9m8ae" } } 01. 02. 03. 04. 05. 06. 07.
  • 46. JS / Hot Reload
  • 47. JS / Hot Reload class Foo { constructor(value) { this.value = value; } log() { console.log(`Foo: ${this.value}`, this instanceof Foo); } } 01. 02. 03. 04. 05. 06. 07. 08.
  • 48. JS / Hot Reload var foo = new Foo(123); foo.log(); // "Foo: 123", true   replaceClass(Foo, class NewFoo { constructor(value) { this.value = value; } log() { console.log(`NewFoo: ${this.value}`, this instanceof NewFoo); } });   foo.log(); // "NewFoo: 123", true foo instanceof Foo; // true 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14.
  • 49. JS / Hot Reload function replaceClass(OldClass, NewClass) { const newProto = NewClass.prototype; OldClass.prototype.__proto__ = newProto;   // Static Object.keys(NewClass).forEach(name => { OldClass[name] = NewClass[name]; });   // Prototype Object.getOwnPropertyNames(newProto).forEach(name => { OldClass.prototype[name] = newProto[name]; }); } 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14.
  • 50. JS / Hot Reload / Проблема replaceClass(Foo, class NewFoo { /* ... */ });   foo.constructor === Foo; // false 01. 02. 03. 04. 05.
  • 51. JS / Hot Reload / Проблема / Решение —  Webpack — оборачивает создание класса в специальный фраппер —  Использовать обвязку для создания классов, например createClass('MyClassName', {...});
  • 52. Live Coding —  React — Webpack/React Hot loader/Redux —  Angular2 — mgechev/angular2-hot-loader (экспериментально) —  Так же Hot Relaod есть для Ember, Vue, RiotJS и т.п.
  • 56. // app/packages/require.config.js var packages = [{ name: "suggests", location: "packages/suggests" }, { name: "push-manager", location: "packages/push-manager" }]; require.config({packages: packages}); Пакеты: npm/bower/jam jam install suggests --save jam install push-manager --save // app/some-controller.js import suggests from "suggests"; import PushManager from "push-manager";   // ... 01. 02. 01. 02. 03. 04. 05. 06. 07. 08. 09. 01. 02. 03. 04. 05.
  • 60.
  • 61.
  • 62.
  • 63. Заключение —  Документируйте процессы —  Ищите инструменты и создавайте их сами —  Не описывайте «шаги», пишите утилиты, которые их выполнят —  Старайтесь писать универсальные компоненты, неотягощенные сложной логикой