Billing Frontend Ecosystem

Что умеет SASS
чего не умеет CSS
// theme
$themeMainColor: #8e3b4b;
$themeMainTextColor: #a52f47;
// text
$textBaseColor: #404040;
$textWhiteColor: #fff;
$textDisabledColor: #b7b7b7;
$textDarkColor: #8c8c8c;
$textErrorColor: #d92b2b;
$textSuccessColor: #00b359;
$transitionBaseTime: .2s;
$transitionBaseTimingFunction: ease-in;
$transitionSmoothOut: .4s cubic-bezier(.23, 1, .32, 1);
$fontSizeSmall: 12px;
$fontSizeRegular: 14px;
$fontSizeBig: 16px;
$fontSizeGreat: 18px;
$fontSizeTextDotsActions: 30px;
$lineHeightSmall: 1.2;
$lineHeightRegular: 1.4;Что умеет SASS
чего не умеет CSS
.element {
&.is-active {}
&.is-disabled {}
.sub-element {}
}
/* css */
.element {}
.element.is-active {}
.element.is-diabled {}
.element .sub-element {}Что умеет SASS
чего не умеет CSS
%as-link {
background: none;
color: $linkBaseColor;
cursor: pointer;
&:hover,
&:focus {
outline: none;
box-shadow: none;
color: $linkActiveColor;
}
}
/* usage */
@extend %as-link;Что умеет SASS
чего не умеет CSS
@import "../mixins";
@mixin windowLessOrEqual($width: 1248px) {
@media only screen and (max-width: $width) {
@content;
}
}
/* usage */
@include windowLessOrEqual(320px) {
font-size: 16px;
};Что умеет SASS
чего не умеет CSS
color: darken(#800, 20%); // => #200Что умеет SASS
чего не умеет CSS
@for $i from 1 through $equalizerBarsCount {
.equalizer__bar:nth-child(#{$i}) {
animation-delay: $i * 10ms + random(200);
}
}P.S.: уникальность

Какой бывает JS

$(document).ready(function() {
$('#b-form').filter_send();
$('a.js-gallerys').fancybox();
$('a.item_gallery_slider').fancybox();
$('a.js-gallery').fancybox({
'titlePosition': 'over'
});
// Метод проверки поля на пустоту, TRUE - если поле пустое
$.validator.addMethod('empty', function(value) {
return (value == '');
});
// Scroll to top
$(window).scroll(function() {
var t = $(document).scrollTop();
var x = $('.l-wrapper').offset().top;
if (t >= x) {
$('#move-to-top').fadeIn();
} else {
$('#move-to-top').fadeOut();
}
});
// Colorbox
$('.b-colorbox').colorbox({
title: function() {
return $(this).parent().find('.b-album-images__desc').html();
},
maxHeight: "98%"
});
$('.b-colorbox--html').colorbox({
inline: true,
onOpen: function() {
$("#colorbox").addClass("colorbox-inline-block");
}
});
$('.main-colorbox').colorbox({
rel: 'gallery',
title: function() {
return $(this).parent().find('.b-album-images__desc').html();
},
maxHeight: "98%"
});
$('.js-mainpage-filter__link').on('click', function(e) {
e.preventDefault();
$('.js-mainpage-filter').slideToggle('fast');
// $('.js-mainpage-catalog').hide('fast');
});
$('.js-mainpage-catalog__link').on('click', function(e) {
e.preventDefault();
$('.js-mainpage-catalog').slideToggle('fast');
// $('.js-mainpage-filter').hide('fast');
});
$('.js-items-on-page').on('change', function() {
$(this).closest('.js-itemspage').submit();
});
wrap_content_images();
});Base.JS

var ActionButtonControl = ButtonControlBase.extend({
constructor: function(id, view) {
this.base(id, view || new ActionButtonControl.View(id));
this._href = null;
this.onClick = Event.create();
this.__attachEventListeners();
},
__attachEventListeners: function() {
var _this = this;
this._view.onClick(function(evt) {
if (_this._isEnabled)
_this.onClick(evt);
else
evt.preventDefault();
});
},
getHref: function () {
return this._href || (this._href = this._view.getHref());
}
}, {
View: ButtonControlBase.View.extend({
constructor: function (id, settings) {
this.base(id, settings);
this.onClick = Event.create();
this.__attachEventListeners();
},
__attachEventListeners: function() {
var _this = this;
this._node.on("click.actionClick", function(evt) {
_this.onClick(evt);
});
},
getHref: function () {
return this._node.find("a").attr("href");
}
})
});EcmaScript 6 (aka 2015)

Тысячи изменений
- let, const
- function (a = 10) {/* ... */}
- class
- import, export modules
- for ... of
- () => {}
- ...obj
- function({a, b}) {/* ... */}
- Promise
- function*
- async/await
- Map, WeakMap, Set, WeakSet
- Iterator protocol
- и другие...

...
const user = {
name: "Maybel",
post: "Sister",
clothes: "Sweater"
};
const { name, post } = user;
// same as
const name = user.name;
const post = user.post;
...
const user = {
name: "Dipper",
post: "Brother",
clothes: "T-shirt"
};
const logger = ({ name, post }) => {
console.log(post, name);
};
logger(user);
Pyramid of doom
someAsyncOp(function() {
anotherAsyncOp(function(a) {
andAnotherAsyncOp(function(b) {
okayEnough(function(c) {
complete(d);
});
});
});
});
Promise
someAsyncOp()
.then(a => { anotherAsyncOp(a) })
.then(b => { andAnotherAsyncOp(b) })
.then(c => { okayEnough(c) })
.then(d => { complete(d) })
.catch(e => { handleError(e) });
Generator
function* handle() {
const a = yield someAsyncOp();
const b = yield anotherAsyncOp(a);
const c = yield andAnotherAsyncOp(b);
const d = yield okayEnough(c);
const e = yield complete(d);
}
async/await
async function handle() {
const a = await someAsyncOp();
const b = await anotherAsyncOp(a);
const c = await andAnotherAsyncOp(b);
const d = await okayEnough(c);
const e = await complete(d);
}
Общая архитектура?
React
React
Компонент
React.DOM.h1(null, 'Hello');<h1>Hello</h1>React
Составляющие
// Element
<h1>Hello!</h1>
// Component
<LightboxHeader>Hello!</LightboxHeader>React
props
Link.propTypes = {
href: PropTypes.string,
children: PropTypes.node,
className: PropTypes.string,
disabledClassName: PropTypes.string,
disabled: PropTypes.bool
};React
Жизненный цикл
// Mounting
componentWillMount()
componentDidMount()
shouldComponentUpdate()
// Updating
componentWillReceiveProps()
componentWillUpdate()
componentDidUpdate()
// Unmounting
componentWillUnmount()React
state компонента
this.setState({
isActive: true
});React
Пример компонента
class TextArea extends Component {
state = {
height: null
};
constructor(props) {
super(props);
const { minHeight } = props;
this.state = {
height: minHeight
};
/*...*/
}
/*...*/
componentDidMount() {
this._changeTextAreaNodeHeight();
}
componentDidUpdate() {
this._changeTextAreaNodeHeight();
}
/*...*/
render() {
const { height } = this.state;
const textInputProps = { ...this.props };
delete textInputProps.minHeight;
delete textInputProps.inputClassName;
return (
<TextInput isTextArea={true}
inputClassName={cx(styles.textArea, this.props.inputClassName)}
height={height}
{...textInputProps}
onChange={this.handleChange}
ref={ (el) => { this._textArea = el }} />
);
}
}
TextArea.propTypes = {
minHeight: PropTypes.number,
onChange: PropTypes.func,
/*...*/
readonly: PropTypes.bool,
disabled: PropTypes.bool,
value: PropTypes.string,
/*...*/
};
TextArea.defaultProps = {
minHeight: 30
};Flux
Реализации
- flux
- reflux
- redux
- alt
- flummox
- fluxible
- fluxxor
- marty.js
- fynx
- McFly
- DeLorean.js
- fluxify
- MobX
Flux

Redux
Redux
Единый store

Redux
Actions, Action Creators

Redux
Reducers

Redux

Redux
Связь с React
- <Provider>
- connect()
Routing

Routing

Routing
<Route path="ExternRegistrationErrors" component={ExternRegistrationErrorsPage}>
<IndexRoute component={AdminErrorsList} />
<Route path="users" component={UsersErrorsList} />
<Route path="permissions" component={PermissionsErrorsList} />
<Redirect from="*" to="ExternRegistrationErrors" />
</Route>Saga

Sagas
function* validateStepWatcher() {
let task = null;
const actionsToWatch = [
actionTypes.PAYMENT_DOCUMENTS_TOGGLE,,
actionTypes.PHYSICAL_CLIENT_CHANGE,
actionTypes.PHYSICAL_CLIENT_ENDING_CHANGE
];
yield* takeLatest(actionsToWatch, function*() {
/* ... */
task = yield fork(fetchData,
validateStepFormUrl,
{ ...printForm, printStep: currentStep },
actions.validateStepBegin,
actions.validateStepSuccess,
actions.validateStepError
);
});
}Общая архитектура?

Как это все собирается
- "Старый код"
- "Новый код"
Как это все собирается
Старый код

Как это все собирается
Старый код
- Gulp: SASS -> CSS
- и причесывание стилей
- MVC bundler: объединение и сжатие

Как это все собирается
Новый код

Как это все собирается
Новый код

- webpack: сборка js в бандлы
- стили туда же
- babel: транспайлинг

Build
- Установка пакетов
- Сборка JS (webpack)
- Сборка стилей (gulp)
Обеспечение качества

Обеспечение качества
Stylelint
ESLint
Mocha
Обеспечение качества
- Редюсеры
- Компоненты
- Хелперы
- Селекторы
- Саги
Тесты
Обеспечение качества
Мечты: деплой
- На pre-commit прогонять lint, comb
- На pre-push прогонять тесты
Занимательное
-
http://bit.ly/billy_front
Billing Frontend Ecosystem
By shimmy
Billing Frontend Ecosystem
- 1,471
