Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Владимир Гриненко
Search
FrontFest
November 21, 2017
Programming
740
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Владимир Гриненко
FrontFest
November 21, 2017
More Decks by FrontFest
See All by FrontFest
Тим Чаптыков
frontfest
0
1.2k
Егор Банщиков
frontfest
0
900
Jose M. Perez
frontfest
0
760
Алексей Иванов
frontfest
0
1.4k
Екатерина Пригара
frontfest
0
720
Léonie Watson
frontfest
0
780
Кирилл Чернышев
frontfest
0
770
Виктор Грищенко
frontfest
0
660
Игорь Алексеенко
frontfest
0
510
Other Decks in Programming
See All in Programming
AIの中の人になってみる
htkym
0
170
App Storeの外へ──日本のiOSサイドローディング入門 for iOSDC Japan 2026
yuukiw00w
0
160
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
160
巨大モノリシックアプリ モダン化大作戦
ktcryomm
0
540
【DroidKaigi 2026】「アクセシビリティを利用するとき、 アクセシビリティもまたこちらを利用している」 〜マルウェアによる攻撃と防衛について〜
halunoyo
0
440
AWS Step Functions 大規模並列の壁を越える / jaws-sonic-2026-niigata-step-functions
kasacchiful
PRO
1
430
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
280
Gmail/Google DriveをトリガーにAIエージェントを動かそう! / Run AI agents with Gmail/Google Drive as triggers!
har1101
3
490
kubernetes コンポーネント開発入門 / 新卒N年目の勉強会&交流会!〜〇〇への誘い〜 #n_study
mazrean
0
200
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
140
AIは賢い。でも実行環境は? CLIおじさんがAI時代に伝えたいこと ~ CLIおじさんがAI時代に伝えたいこと ~
curekoshimizu
1
230
WebMCP Challenge に星空観察アプリで参加した話
okajun35
0
130
Featured
See All Featured
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
57k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
690
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.1k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
330
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
270
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
660
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
680
A Tale of Four Properties
chriscoyier
163
24k
Transcript
Декларативная шаблонизация Владимир Гриненко руководитель службы
Декларативность
Декларативное программирование — это парадигма программирования, в которой задается спецификация
решения задачи, то есть описывается, что представляет собой проблема и
4
Императивно
document Императивно 6
document .querySelectorAll('.link') Императивно 7
document .querySelectorAll('.link') .forEach(link => link.style.color = 'red'); Императивно 8
document .querySelectorAll('.link') .forEach(link => link.style.color = 'red'); document.body.innerHTML += '<a
class="link" href="http://2017.frontfest.ru">FrontFest</a>'; Императивно 9
document .querySelectorAll('.link') .forEach(link => link.style.color = 'red'); document.body.innerHTML += '<a
class="link" href="http://2017.frontfest.ru">FrontFest</a>'; Императивно 10 document.querySelector('.link').classList.remove('link');
Декларативно
.link { color: red } Декларативно 12
library/components/link.css .link { color: red } Декларативно 13 project/components/link.css .link
{ text-decoration: none }
$('.btn').click(function() { $(this) .toggleClass('btn_disabled') .text( $(this).text() === 'Enable' ? 'Disable'
: 'Enable' ); }); Императивно 14
<Btn onToggleDisabled={this.handleToggleDisabled} disabled={this.state.disabled}> {this.state.buttonText} </Btn> Декларативно 15
Шаблонизация
<div class="user"> Elizabeth </div> Шаблонизация 17
<div class="user"> <?php echo $username; ?> </div> Шаблонизация 18
const html = `<div class="user">${username}</div>`; Шаблонизация 19
20
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> Dropdown button </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Another action</a> <a class="dropdown-item" href="#">Something else here</a> </div> </div> Шаблонизация 21
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> <?php echo $buttonText; ?> </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <?php for ($i = 1; $i <= count($items); $i++) { ?> <a class="dropdown-item" href="<?php echo $items[$i] ["href"]; ?>"><?php echo $items[$i]["text"]; ?></a> <?php } ?> </div> </div> Шаблонизация 22
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> <?php echo $buttonText; ?> </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <?php for ($i = 1; $i <= count($items); $i++) { ?> <a class="dropdown-item" href="<?php echo $items[$i] ["href"]; ?>"><?php echo $items[$i]["text"]; ?></a> <?php } ?> </div> </div> Шаблонизация 23
Проблемы 24 〉Много писать 〉Много исправлять 〉Невозможно обновиться автоматически
const html = `<div class="user">${username}</div>`; Шаблонизация 25
render() { return ( <div className="user"> {this.props.username} </div> ); }
Компонентный подход 26
<User username="Elizabeth"/> Компонентный подход 27
Проблемы решены 28 〉Много писать 〉Много исправлять 〉Невозможно обновиться автоматически
render() { return ( <div className="user"> {this.props.username} </div> ); }
Еще проблемы 29
render() { return ( <div className="user"> {this.props.hasIcon && <Icon/>} {this.props.username}
</div> ); } Еще проблемы 30
library/components/link.css .link { color: red } Декларативно 31 project/components/link.css .link
{ text-decoration: none }
32
Больше декларативности!
XML + XSLT
<User username="Elizabeth"/> <User username="Elizabeth II" hasIcon="true"/> XML + XSLT 35
<xsl:template match="User"> <div class="user"> <xsl:apply-templates/> <xsl:apply-templates select="@username"/> </div> </xsl:template> XML
+ XSLT 36
<xsl:template match="User[hasIcon=true]"> <xsl:apply-templates select="Icon"/> </xsl:template> XML + XSLT 37
BEMJSON + BEMHTML
{ block: 'user', username: 'Elizabeth' } Полная декларативность 39
{ block: 'user', username: 'Elizabeth' } Полная декларативность 40 <div
class="user"></div>
block('user').content()(node => node.ctx.username); Полная декларативность 41
block('user').content()(node => node.ctx.username); block('user') .match()(node => node.ctx.hasIcon) .content()(node => [
{ block: 'icon' }, applyNext() ]); Полная декларативность 42
block('user').content()(node => node.ctx.username); block('user') .match()(node => node.ctx.hasIcon) .content()(node => [
{ block: 'icon' }, applyNext() ]); Полная декларативность 43
block('user').content()(node => node.ctx.username); block('user') .match()(node => node.ctx.hasIcon) .content()(node => [
{ block: 'icon' }, applyNext() ]); Полная декларативность 44
block('data')( tag()('ul'), elem('item').tag()('li') ); Полная декларативность: теги 45
block('data')( tag()('table'), content()(() => ({ tag: 'tr', content: applyNext() })),
elem('item').tag()('td') ); Полная декларативность: теги 46
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> Dropdown button </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Another action</a> <a class="dropdown-item" href="#">Something else here</a> </div> </div> Полная декларативность: реальный пример 47
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> <?php echo $buttonText; ?> </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <?php for ($i = 1; $i <= count($items); $i++) { ?> <a class="dropdown-item" href="<?php echo $items[$i] ["href"]; ?>"><?php echo $items[$i]["text"]; ?></a> <?php } ?> </div> </div> Полная декларативность: реальный пример 48
<div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-
haspopup="true" aria-expanded="false"> <?php echo $buttonText; ?> </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <?php for ($i = 1; $i <= count($items); $i++) { ?> <a class="dropdown-item" href="<?php echo $items[$i] ["href"]; ?>"><?php echo $items[$i]["text"]; ?></a> <?php } ?> </div> </div> Полная декларативность: реальный пример 49
block('dropdown')( content()(node => [ { block: 'btn', mods: { secondary:
true }, mix: 'dropdown-toggle', attrs: { type: 'button', id: 'dropdownMenuButton', 'data-toggle': 'dropdown', 'aria-haspopup': true, 'aria-expanded': false } }, { elem: 'menu', attrs: { 'aria-labelledby': 'dropdownMenuButton' }, content: applyNext().map(item => ({ elem: 'item', url: item.url, content: item.text })) } ]), Полная декларативность: реальный пример 50 elem('item').replace()(node => ({ block: 'link', mix: node.ctx, url: node.ctx.url, content: node.ctx.content })) )
{ block: 'dropdown', content: [ { elem: 'item', url: 'http://site1.com',
text: 'Site1' }, { elem: 'item', url: 'http://site2.com', text: 'Site2' }, { elem: 'item', url: 'http://site3.com', text: 'Site3' } ] } Полная декларативность: реальный пример 51
const css = require('./css.json'), visited = {}; block('*') .match()(node =>
!visited[node.block]) .replace()(node => { visited[node.block] = true; return [ css[node.block], this.ctx ]; }); Полная декларативность: любой узел 52
53 bem.github.io/bem-xjst
54 github.com/bem/bh-php
$bh->match('button', function ($ctx) { $ctx->tag('button'); }); bh.php 55
Синтаксис может быть любым
React
bem-react-core
Что это 59 〉Обертка над стандартными React-компонентами 〉Полностью совместимая со
стандартными компонентами 〉Не требует знания БЭМ 〉Не требует использования модификаторов 〉Не требует использования уровней переопределения
Что это 60 〉Автоматизирует генерацию классов 〉Позволяет использовать элементы, модификаторы
и миксы 〉Позволяет использовать уровни переопределения 〉Оперирует компонентами, а не путями
None
<div class="user"> Elizabeth </div> Пример 62
export default class User extends Component { render() { return
( <div className="user">{this.props.username}</div> ); } } Было 63
import {decl} from 'bem-react-core'; export default decl({ block: 'user', content({username})
{ return username; } }); Стало 64
export default decl({ block: 'User', willMount() {}, didMount() {}, willReceiveProps()
{}, shouldUpdate() {}, willUpdate() {}, didUpdate() {}, willUnmount() {} }); Livecycle-методы 65
import User from 'b:user'; ReactDOM.render( <User />, //... ); Импорт
66
var User = [ require('path/to/user/user.js') ][0].applyDecls(); ReactDOM.render(<User />); Импорт 67
Траншпилеры 68 〉webpack-bem-loader 〉babel-plugin-bem-import
Какие проблемы решает bem-react-core
Какие проблемы 70 〉Модификации бывают опциональными › отдельные файлы ›
можно выразить через class ButtonWithIcon extends Button
Какие проблемы 71 〉Модификации бывают опциональными 〉Композитные модификации › пересечение
всех вариантов не выражается через наследование › Button, Button_compound, Button_with-icon
import { BaseComponent, warn } from '../../Utilities'; import { ButtonType,
IButtonProps } from './Button.Props'; import { DefaultButton } from './DefaultButton/DefaultButton'; import { CommandButton } from './CommandButton/CommandButton'; import { CompoundButton } from './CompoundButton/CompoundButton'; import { IconButton } from './IconButton/IconButton'; import { PrimaryButton } from './PrimaryButton/PrimaryButton'; Например, кнопка 72 github.com/OfficeDev/office-ui-fabric-react
public render() { const props = this.props; switch (props.buttonType) {
case ButtonType.command: return <CommandButton { ...props } />; case ButtonType.compound: return <CompoundButton { ...props } />; case ButtonType.icon: return <IconButton { ...props } />; case ButtonType.primary: return <PrimaryButton { ...props } />; default: return <DefaultButton { ...props } />; } } Например, кнопка 73
<Button compound primary icon="check"></Button> А теперь с bem-react-core 74 Декларативно!
// Button_type_compound.js import {declMod} from 'bem-react-core'; import React from 'react';
export default declMod(({ type }) => type === 'button', { block: 'Button', mods({ type }) { return { ...this.__base.apply(this, arguments), type }; }, content() { return <Button … /> } }); Модификатор 75
import Button from 'b:Button m:type=compound'; <Button type="compound">my button</Button> Модификатор 76
None
Шаблонизация в bem-react-core 78 〉tag 〉attrs 〉mods 〉cls 〉mix 〉content
Уровни переопределения
Для чего 80 〉Общая библиотека и проекты
Для чего 81 〉Общая библиотека и проекты lego serp images
video news
Для чего 82 〉Общая библиотека и проекты 〉Платформы
Для чего 83 〉Общая библиотека и проекты 〉Платформы common desktop
touch
Для чего 84 〉Общая библиотека и проекты 〉Платформы common desktop
touch pad phone
Для чего 85 〉Общая библиотека и проекты 〉Платформы 〉Дизайн
Для чего 86 〉Общая библиотека и проекты 〉Платформы 〉Дизайн 〉Эксперименты
experiments exp1 exp2 exp3
None
library/components/link.css .link { color: red } Как это работает 88
project/components/link.css .link { text-decoration: none }
[ 'path/to/level1', 'path/to/level2' ] Уровни переопределения 89
/* styles.css */ @import path/to/level1/block1/block1.css; @import path/to/level2/block1/block1.css; Уровни переопределения 90
// blocks/Link/Link.js import decl from 'bem-react-core'; export default decl({ block:
'Link', tag: 'a', attrs(({url}) => ({href: url})) }); <Link url="…">… <a class="Link" href="…">… Уровни переопределения 91
// blocks/Link/Link.js // a11y/blocks/Link/Link.js import decl from 'bem-react-core'; export default
decl({ block: 'Link', attrs() { return { ...this.__base.apply(this, arguments), role: 'link' }; } } <Link url="…">… <a class="Link" href="…" role="link">… Уровни переопределения 92
None
94 github.com/bem/bem-react-core
95 Пишите декларативно!
None
Вопросы?
None
Владимир Гриненко
[email protected]
tadatuta tadatuta tadatuta