## Архитектура web-приложений
Слайды доступны по ссылке
frontend.tech-mail.ru

## Архитектура

Какие-то определения с Википедии...

Если рассматривать приложение как систему — т.е. набор компонентов, объединенных для выполнения определенной функции:

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

> Архитектура — это организация системы, воплощенная в её компонентах, их отношениях между собой и с окружением.

## Архитектурные решения

Как определить, является ли какое-то решение архитектурным. Как определить качество архитектурного решения?
Задайте себе вопрос:

> А что если я ошибся и мне придется изменить это решение в будущем? Какие будут последствия?

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

## Проектирование системы

Критерии хорошего дизайна системы

- Эффективность системы
- Гибкость и расширяемость системы
- Масштабируемость процесса разработки
- Тестируемость и сопровождаемость
- Возможность повторного использования

## Проектирование системы

Критерии плохого дизайна системы

- Его тяжело изменить, поскольку любое изменение влияет на слишком большое количество других частей системы
- При внесении изменений неожиданно ломаются другие части системы
- Код тяжело использовать повторно в другом приложении, поскольку его слишком тяжело «выпутать» из текущего приложения

## Методологии и принципы
проектирования ПО

- DRY — don’t repeat yourself (не повторяйте себя) — пфф, изи!!!
- KISS — keep it simple stupid (делайте вещи проще) — проще простого!
- YAGNI — you ain’t gonna need it (вам это не понадобится) — действительно
- GRASP — документированные и стандартизированные принципы объектно-ориентированного анализа — ну как скажешь...
- S.O.L.I.D. — пять основных принципов объектно-ориентированного программирования и проектирования — эээээ, океееей...
- PACKAGE PRINCIPLES — package cohesion (REP, CRP и CCP) и package coupling (ADP, SDP и SAP) — ........

0LHRg9C00YMg0L/RgNC+0YHRgtC40YLRg9GC0LrQvtC5 (base64)

## Методологии и принципы
проектирования ПО

Где про всё это почитать?

- Про различные принципы здесь
- Про GRASP на вики
- Про S.O.L.I.D. — тоже на вики
- Package principles — вообще высший пилотаж (но будучи трезвым читать не советую)

## S.O.L.I.D-ый JavaScript open_in_new

## Как же создать хорошую архитектуру?

## Использовать проверенные подходы!

## Декомпозиция как основа
модульной архитектуры

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

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

## Хорошая декомпозиция должна быть функциональной

## Хорошая декомпозиция должна
быть функциональной

Во главе функциональной декомпозиции лежит паттерн Модуль

> Модуль — это Функция + Данные, необходимые для её выполнения

- Инкапсуляция данных
- Явное управление зависимостями (создание чёткой структуры подключаемых модулей)
- Уход от засорения глобального контекста

## Простейший модуль на основе
самовызывающейся функции

    		(function () {
    		    const data1 = ... ; const data2 = ... ;  // локальные переменные
    		    const Base = window.Base; // импортируем модуль
    		    class Module extends Base {
    		        constructor () { ... }
    		        do () {
    		            console.log(data1, data2);
    		        }
    		    }

    		    window.Module = Module; // экспортируем модуль
    		})();

## Простейший модуль на основе
самовызывающейся функции

    		// Использование
    		const Module = window.Module; // импортируем модуль
    		const m1 = new Module();
    		const m2 = new Module();
    		// ...

    		m1.do();
    		// ...

## Используем всего лишь одну
глобальную переменную!

    		(function (modules) {
    		    const Base = modules.Base; // импортируем модуль
    		    class Module extends Base {
    		        constructor () { ... }
    		        do () { ... }
    		    }

    		    modules.Module = Module; // экспортируем модуль
    		})(window.___all_modules);

## Множество систем определения
модулей

Почти все из них здесь

- Простейшая через IIFE
- CommonJS Modules — используется в Node.js
- AMD Asynchronous Module Definition — используется в очень популярной библиотеке RequireJS, и встречается в большом количестве "взрослых" проектов
- UMD Universal Module Definition — обёртка-хамелеон, используется для определения модулей как в CommonJS-, так и для AMD-форматов
- ES2015 Modules — часть стандарта языка JavaScript

## PRO TIPs использования модулей

- ОДИН файл — ОДИН модуль — самое главное правило, если появляется необходимость определить в одном файле несколько модулей, значит вы что-то делаете не так
- Не допускать циклические зависимости — есть специальные алгоритмы избавления от них
- Не создавайте утилитарные мега-модули с множеством экспортируемых функций — создавайте просто набор маленьких функций-модулей

## Антон Немцев — Паттерны JavaScript open_in_new

## Хорошая декомпозиция должна создавать слабосвязанную систему

## Парочка определений

> Internal Cohesion — сопряженность или «сплоченность» внутри модуля (составных частей модуля друг с другом)

> External Coupling — связанность взаимодействующих друг с другом модулей.

## Хорошая декомпозиция должна
создавать слабосвязанную систему

Модули, полученные в результате декомпозиции, должны быть максимально сопряженны внутри (high internal cohesion) и минимально связанны друг с другом (low external coupling)

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

## Ослаблять связанность нужно на
всех уровнях иерархии

- Какую функцию выполняет каждый модуль?
- Насколько модули легко тестировать?
- Возможно ли использовать модули самостоятельно или в другом окружении?
- Как сильно изменения в одном модуле отразятся на остальных?

## Способы снижения связанности
модулей

- Использование паттернов ООП, интерфейсы, фасад
- Использование Dependency Inversion — корректное создание и получение зависимостей
- Замена прямых зависимостей на обмен сообщениями
- Замена прямых зависимостей на синхронизацию через общее ядро
- Следование Закону Деметры (law of Demeter), запрет неявных и транзитивных зависимостей
- Композиция вместо наследования

## Паттерн Observable

Интерфейс подписки на события

    		// event-emitter
    		class SomeService {
    		    constructor () { ... }
    		    do () { ... }

    		    addEventListener(event, callback) { ... }
    		    removeEventListener(event, callback) { ... }
    		    emitEvent(eventName, eventData) { ... }
    		}

## Паттерн Observable

Интерфейс подписки на события

    		// on-off
    		class SomeService {
    		    constructor () { ... }
    		    do () { ... }

    		    on(event, callback) { ... }
    		    off(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}

## Паттерн Observable

Интерфейс подписки на события

    		// pub-sub
    		class SomeService {
    		    constructor () { ... }
    		    do () { ... }

    		    subscribe(event, callback) { ... }
    		    unsubscribe(event, callback) { ... }
    		    publish(eventName, eventData) { ... }
    		}

## Паттерн Observable

Интерфейс подписки на события

    		// event-dispatcher
    		class SomeService {
    		    constructor () { ... }
    		    do () { ... }

    		    attachEvent(event, callback) { ... }
    		    detachEvent(event, callback) { ... }
    		    dispatchEvent(eventName, eventData) { ... }
    		}

## Реализация паттерна Observable

    		class SomeService {
    		    on(event, callback) {    // подписываемся на событие
    		        this.listeners[event].push(callback)
    		    }
    		    off(event, callback) {   // отписываемся от события
    		        this.listeners[event] = this.listeners[event]
    		            .filter(function (listener) { return listener !== callback; })
    		    }
    		    emit(event, data) {      // публикуем (диспатчим, эмитим) событие
    		        this.listeners[event].forEach(function (listener) {
    		            listener(data)
    		        })
    		    }
    		}

## Реализация паттерна Observable

    		const service = new SomeService();
    		// функция-обработчик события
    		const onload = function (data) { console.log(data); }

    		// подписываемся на событие
    		service.on('loaded', onload);
    		service.emit('loaded', {data: 42});    // событие 1
    		service.emit('loaded', {foo: 'bar'});  // событие 2
    		// отписываемся от события
    		service.off('loaded', onload);

## Итак, как это помогает уменьшить связность приложения?

## Что было до?

    		// services/user-service.js
    		class UserService {
    		    auth (login, password) {
    		        return HTTP.POST('/api', {login, password})
    		            .then(function (user) {
    		                this.user = user;
    		                this.menuSection.reRender();
    		                this.signupSection.hide();
    		                this.profileSection.show(user);
    		                // ...
    		            }.bind(this));
    		    }
    		}

## Что было до?

    		// blocks/scoreboard.js
    		const UserService = window.UserService;

    		class ScoreBoardBlock {
    		    constructor () {
    		        this.userService = new UserService();
    		    }

    		    render (login, password) {
    		        const users = this.userService.getUsers();
    		        this.el.innerHTML = tmpl({users: users});
    		    }
    		}

## Как станет?

    		// services/user-service.js
    		class UserService {
    		    auth (login, password) {
    		        return HTTP.POST('/api', {login, password})
    		            .then(function (user) {
    		                this.user = user;
    		                this.emit('signed', user);
    		                // всё!
    		            }.bind(this));
    		    }
    		    on(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}

## Как станет?

    		// blocks/scoreboard.js
    		class ScoreBoardBlock {
    		    onUsersLoaded (users) {
    		        this.el.innerHTML = tmpl({users: users});
    		    }
    		}

    		// main.js
    		UserService.on('signed', menuSection.reRender.bind(menuSection));
    		UserService.on('users-loaded', scoreboard.onUsersLoaded.bind(scoreboard));
    		// ...
    		UserService.auth('login', 'password');

## В чём преимущество?

- Все Observable-модули реализуют один и тот же интерфейс методов
- Модули, на которые мы подписываемся, сами знают, когда и какие события эмитить
- Модули, которые подписываются, сами знают, как обрабатывать эти события
- И те, и другие, ничего друг о друге не знают, модулям известны только названия и формат сообщений
- Все необходимые подписки происходят в одном месте (`main.js`)
- Удобно оповещать систему о новых событиях

## А если таких Observable-модулей очень много?

## Реализовывать паттерн Observable
в каждом модуле?

    		// плохо... дублирование кода
    		class UserService {
    		    on(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}
    		class GameService {
    		    on(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}
    		class ShopService {
    		    // ...
    		}

## Выделить Observable в отдельный
модуль и отнаследоваться от него?

    		class UserService extends Observable {
    		    // ...
    		}
    		class GameService extends Observable {
    		    // ...
    		}

    		// а если таких модулей-интерфейсов будет несколько?
    		class ShopService extends Observable, Loggable, Serializable {
    		    // ...
    		}

## Паттерн Примесь (Mixin)

> Примесь (англ. mixin) — класс или объект, реализующий какое-либо чётко выделенное поведение. Используется для уточнения поведения других классов, не предназначен для самостоятельного использования. Решают проблему множественного наследования. Отличаются от интерфейсов тем, что содержат готовый функционал, а не всего лишь специфицирует поведение

Подробнее в статье по ссылке и на learn.javascript.ru

## Использование примеси

    		const ObservableMixin = {
    		    on(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		    // ...
    		}
    		class UserService {
    		    constructor () {
    		        this.emit('event', { ... });
    		        // ...
    		    }
    		}
    		Object.assign(UserService.prototype, ObservableMixin);

## А что кроме наследования?

## Как создать класс МФУ?

    		class ЧБПринтер {
    		    print (doc) { ... }
    		}

    		class ЦветнойПринтер {
    		    print (doc) { ... }
    		}

    		class Сканер {
    		    scan (doc) { ... }
    		}

## Наследование Композиция!

    		class МФУ {
    		    constructor () {
    		        this.чбПринтер = new ЧБПринтер();
    		        this.цветнойПринтер = new ЦветнойПринтер();
    		        this.сканер = new Сканер();
    		        // ...
    		    }

    		    scan (doc) {
    		        return this.сканер.scan(doc);
    		    }
    		}

## А если будет много Observable-модулей?

## Паттерн медиатор

> Когда в системе присутствует большое количество модулей, их прямое взаимодействие друг с другом (даже с учётом применения publish-subscriber подхода) становится слишком сложным. Поэтому имеет смысл взаимодействие «все со всеми» заменить на взаимодействие «один со всеми». Для этого вводится некий обобщенный посредник — медиатор

## Паттерн медиатор

    		// modules/event-bus.js
    		export default class EventBus {
    		    on(event, callback) { ... }
    		    off(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}

    		// main.js
    		import EventBus from './modules/event-bus.js';
    		window.bus = new EventBus();

## Паттерн медиатор

    		// services/user-service.js
    		export default class UserService {
    		    login () {
    		        HTTP.post({ ... })
    		            .then(function (user) {
    		                // do stuff

    		                window.bus.emit('user:logged-in', user);
    		            })
    		    }
    		}

## Паттерн медиатор

    		// blocks/user-profile.js
    		export default class UserProfile {
    		    constructor () {
    		        window.bus.on('user:logged-in', function (user) {
    		            // do stuff

    		            this.render();
    		        }.bind(this))
    		    }
    		}

## Глобальные переменные — зло!

## Паттерн синглтон

    		// modules/event-bus.js
    		class EventBus {
    		    on(event, callback) { ... }
    		    off(event, callback) { ... }
    		    emit(eventName, eventData) { ... }
    		}

    		export default new EventBus();

## Паттерн синглтон

    		// blocks/user-profile.js
    		import bus from '../modules/event-bus.js';

    		export default class UserProfile {
    		    constructor () {
    		        bus.on('user:logged-in', function (user) {
    		            // do stuff

    		            this.render();
    		        }.bind(this))
    		    }
    		}

## А можно и вот так

    		// modules/event-bus.js
    		export default class EventBus {
    		    constructor () {
    		        if (EventBus.__instance) {
    		            return EventBus.__instance;
    		        }
    		        // initialization logic

    		        EventBus.__instance = this;
    		    }
    		}

## А можно и вот так

    		// main.js
    		import EventBus from './modules/event-bus.js';

    		const bus1 = new EventBus();
    		const bus2 = new EventBus();

    		bus1 === bus2; // true

## PRO TIPs хорошей декомпозиции

- Минимизировать использование глобальных зависимостей
- Не использовать неявные зависимости — все зависимости модуля должны быть обозначены явно
- Минимизировать связи между модулями — например, с помощью паттернов Observable и Mediator

## Какие ещё есть подходы?

- Inversion of Control и Dependency Injection — найти хороший материал оказалось сложновато, но вот, например, или здесь
- Event Driven Development — статейка на хабре
- Ещё много всего, творите!

## Как проектировать хорошую архитектуру open_in_new
очень крутая статья

## Шаблоны MV*

## MV-паттерны для проектирования
веб-приложений

## Шаблон MVC

> Шаблон MVC (Модель-Вид-Контроллер или Модель-Состояние-Поведение) описывает простой способ построения структуры приложения, целью которого является отделение бизнес-логики от пользовательского интерфейса. В результате, приложение легче масштабируется, тестируется, сопровождается и, конечно же, реализуется

## Модели в MVC

- Cодержит бизнес-логику приложения: методы для получения и обработки данных
- Не взаимодействуют с напрямую пользователем
- Не генерируют никакого HTML (не управляют отображением данных)
- Модели могут хранить в себе данные и они могут взаимодействовать с другими моделями

## Представления (aka вьюхи) в MVC

- Отвечают за отображение данных, содержат в себе вызовы шаблонизаторов, создание блоков и компонентов и всего такого
- Получают данные от напрямую от моделей или от контроллеров
- Взаимодействуют с моделями посредством контроллеров
- Являются посредниками между пользователем и контроллером

## Контроллеры в MVC

- Являются связующим звеном приложения
- Реализуют взаимодействие между вьюхами и моделями и взаимодействие вьюх друг с другом
- Должны содержать минимум бизнес-логики и быть максимально простыми в конфигурации (для возможности удобного изменения и расширения приложения)
- Логика контроллера довольно типична и большая ее часть выносится в базовые классы, в отличие от моделей, логика которых, как правило, довольно специфична для конкретного приложения

## Собираем всё воедино

## Как нам сделать шаг в
сторону MVC?

- Создаём базовый класс `View`
- Наследуем от него `MenuView, SignView, ScoreboardView...`
- Во вьюхах описываем описываем отображение определённой части приложения
- Содержимое `View` генерируется с помощью шаблонизатора
- Далее на основе уже имеющейся разметки создаются блоки и компоненты, либо они генерируются динамически на каком-то этапе жизненного цикла приложения
- Каждый момент времени активна только одна `View`

## Как нам сделать шаг в
сторону MVC?

- У нас уже имеются сервисы и модули, которые выполняют роли моделей в нашем приложении и описывают бизнес-логику работы с данными
- Настраиваем взаимодействие между частями приложения посредством контроллеров (например, медиатора)
- Во время работы приложения управление передаётся между разными `View`
- Стараемся уменьшить связность приложения, отделив модели и представления друг от друга (проведя правильную декомпозицию)

## Верхнеуровневая архитектура фронтенда open_in_new

## Это серебряная пуля?

## Увы, но не всегда

## Flux

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

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

## Основные понятия

В минимальном варианте Flux-архитектура может содержать три слоя, взаимодействующие по порядку:

- Actions (действия)
- Stores (хранилища)
- Views (представления)
- Обычно между Actions и Stores добавляют Dispatcher (диспетчер)

## Actions

> Action – выражение событий (обычно - простой объект). Диспетчеры передают действия нижележащим компонентам (хранилищам) по одному. Новое действие не передаётся пока предыдущее полностью не обработано компонентами. Действия из-за работы источника действия, например, пользователя, поступают асинхронно, но их диспетчеризация является синхронным процессом. Кроме имени, действия могут иметь полезную нагрузку (payload), содержащую относящиеся к действию данные.

> // {"name": "IncreaseCount", "payload": {"inc": 1}}

## Dispatcher

> Dispatcher предназначен для передачи действий хранилищам. В упрощённом варианте диспетчер может вообще не выделяться, как единственный на всё приложение. В диспетчере хранилища регистрируют свои функции обратного вызова (callback) и зависимости между хранилищами.

## Stores

> Store является местом, где сосредоточено состояние (state) приложения. Остальные компоненты, согласно Flux, не имеют значимого (с точки зрения архитектуры) состояния. Изменение состояния хранилища происходит строго на основе данных действия и старого состояния хранилища (т.е. является чистой функцией)

## Views

> View – компонент, обычно отвечающий за выдачу информации пользователю. Во Flux-архитектуре, которая может технически не касаться внутреннего устройства представлений вообще, это — конечная точка потоков данных. Для информационной архитектуры важно только, что данные попадают в систему (то есть, обратно в хранилища) только через действия

## Собираем все вместе

- Каждое хранилище регистрируется в диспетчере вместе со своими обратными вызовами (callback).
- Когда диспетчер получает действие, то он выполняет обратный вызов, передавая ему поступившее действие в качестве параметра
- В зависимости от типа действия вызывается тот или иной метод внутри хранилища, в котором происходит обновление состояния хранилища
- После обновления хранилища генерируется событие, которое указывает, что хранилище было обновлено. И через это событие представления узнают, что хранилище было обновлено, и сами обновляют свое состояние.

## Собираем все вместе

## Еще немного информации

- Статья от Artalog о state-manager

## Роутинг в web-приложениях

## Вернёмся немного назад

## А если Views очень много?

## Вспоминаем про паттерн "Медиатор"

## Роутер

## Что такое роутинг?

> На сервере роутинг — это процесс определения маршрута внутри приложения в зависимости от запроса. Проще говоря, это поиск контроллера по запрошенному URL и выполнение соответствующих действий

> На клиенте роутинг позволяет установить соответствие между состоянием приложения и `View`, которая будет отображаться. Таким образом, роутинг — это вариант реализации паттерна "Медиатор" для MV* архитектуры приложений

> Кроме этого, роутеры решают ещё одну очень важную задачу. Они позволяют эмулировать историю переходов в SPA-приложениях

## Что такое роутинг?

Таким образом взаимодействие и переключение между `View` происходит посредством роутера, а сами `View` друг о друге ничего не знают

    		Router.register({state: 'main'}, MenuView);
    		Router.register({state: 'signup'}, SignupView);
    		Router.register({state: 'scores'}, ScoreboardView);
    		...
    		// переход на 3 страницу (пагинация)
    		Router.go({state: 'scores', params: {page: 3}});

## Что такое роутинг?

    		class Router {
    		    constructor() { ... }
    		    register(path: string, view: View) { ... }
    		    start() { ... }      // запустить роутер
    		    go(path: string) { ... }
    		    back() { ... }       // переход назад по истории браузера
    		    forward() { ... }    // переход вперёд по истории браузера
    		}

## History API

> History API — браузерное API, позволяет манипулировать историей браузера в пределах сессии , а именно историей о посещённых страницах в пределах вкладки или фрейма, загруженного внутри страницы. Позволяет перемещаться по истории переходов, а так же управлять содержимым адресной строки браузера

## History API

    		// Перемещение вперед и назад по истории
    		window.history.back(); // работает как кнопка "Назад"
    		window.history.forward(); // работает как кнопка "Вперёд"

    		window.history.go(-2); // перемещение на несколько записей
    		window.history.go( 2);

    		const length = window.history.length; // количество записей

## History API

    		// Изменение истории
    		const state = { foo: 'bar' };
    		window.history.pushState(
    		    state,         // объект состояния
    		    'Page Title',  // заголовок состояния
    		    '/pages/menu'  // URL новой записи (same origin)
    		);
    		window.history.replaceState(state2, 'Other Title', '/another/page');

## History API

> Событие `popstate` отсылается объекту `window` каждый раз, когда активная запись истории меняется между двумя записями истории для одного и того же документа

Простой вызов `pushState()` или `replaceState()` не вызовет событие `popstate`. Оно срабатывает только тогда, когда происходят какие-то действия в браузере, такие как нажатие кнопки "назад" (или вызов `history.back()` из JavaScript)

## History API

    		window.onpopstate = event => console.log(location.pathname);

    		history.pushState({ page: 1 }, 'Title 1', '/menu?page=1');
    		history.pushState({ page: 2 }, 'Title 2', '/app?page=2');
    		history.pushState({ page: 3 }, 'Title 3', '/scores?page=3');
    		history.back();   // /app?page=2
    		history.back();   // /menu?page=1
    		history.go(2);    // /scores?page=3

## Что такое роутинг?

    		Router.register('/', MenuView);
    		Router.register('/signup', SignupView);
    		Router.register('/scores/pages/{page}', ScoreboardView);
    		...
    		Router.go('/scores/page/3'); // переход на 3 страницу (пагинация)

## "Архитектура" CSS

## С JS'ом разобрались, а с CSS что?

## Дизайн языка

- Совместимость в обе стороны
- Удобство в поддержке и изменении
- `Простота.` В написании, чтении, реализации идей
- Разнообразие возможностей ради выразительности Интернета
- CSS `слабо связан с HTML` и лишь дополняет его
- `Гибкость` => каскадирование и наследование
- Возможность работать в связке с другими языками. Javascript :)

Подробнее о принципах в документации W3C

## Как стили попадают на страницу

- Браузерные стили: default (user agent) style sheet
Браузерные стили можно переопределять самостоятельно или с помощью normalizecss
- Пользовательские стили (!important)
Необходимо помнить, что они существуют в природе и не препятствовать их применению
- link rel="stylesheet" href=""
- тег style
- аттрибут style
Подходит для использования вместе с JS

## Писать стили — одно удовольствие

    		/* Селекторы! */

    		*                                  /* универсальный селектор */
    		div, span, a                       /* селекторы по имени тегов */
    		.class                             /* селекторы по имени классов */
    		#id                                /* селекторы по идентификаторам */
    		[type="text"], [src*="/img/"]      /* селекторы по атрибутам */
    		:first-child, :visited, :nth-of-type(An+B), :empty ...
    		::before, ::placeholder, ::selection, ::first-letter ...
    		a > a, a + a , a ~ a               /* вложенность и каскадирование */

Можно создавать правила любой степени вложенности — "каскад".

## Как понять,

## какой селектор сработает?

- How is specificity calculated? — MDN
- Взвешиваем селекторы CSS — Хабр
- Калькулятор специфичности селектора

## Какие могут быть проблемы?

Задача: один и тот же компонент должен выглядеть по-разному в зависимости от страницы

    		/* Изменение компонентов в зависимости от родителя */
    		.button { border: 1px solid black; }
    		#sidebar .button { border-color: red; }
    		#header .button { border-color: green; }
    		#menu .button { border-color: blue; }

## Какие могут быть проблемы?

Задача: найти элемент на странице "в слепую"

    		/* Глубокая степень вложенности */
    		#main-nav ul li ul li ol span div { ... }
    		#content .article h1:first-child [name=accent] { ... }
    		#sidebar > div > h3 + p a ~ strong { ... }

Проблема: сильная связанность со структурой документа

## Какие могут быть проблемы?

Задача: сделать стили более понятными для всех

    		/* Широко используемые имена классов */
    		.article { ... }
    		.article .header { ... }
    		.article .title { ... }
    		.article .content { ... }
    		.article .section { ... }

Проблема не очевидна, но она есть!

## Какие могут быть проблемы?

Задача: сделать стили более понятными для всех

    			/* Широко используемые имена классов */
    			.article { ... }
    			.article .header { ... }
    			.article .title { ... }
    			.article .content { ... }
    			.article .section { ... }

Проблема: пересечение имён с внешними библиотеками или даже внутри собственного проекта

## Какие могут быть проблемы?

    		/* Супер классы! */
    		.super-class {
    		  margin: 10px;
    		  position: absolute;
    		  background: black;
    		  color: white;
    		  transition: color 0.2s;
    		  .....
    		}

## Признаки хорошей архитектуры
(всё то же самое)

- Предсказуемость — изменение текущих стилей не ломает проект
- Масштабируемость — добавление новых стилей не ломает проект
- Поддержка — все в команде понимают, как писать стили
- Повторное использование — DRY

Решение? Использовать зарекомендовавшие себя методологии!
Подробнее про них в статье "Способы организации CSS кода"
Доклад о методологиях (альтернативный)
Перечень всех методологий из доклада

## OOCSS — Объектно-ориентированный CSS

## Объектно-ориентированный CSS
OOCSS

Разделение структуры и оформления

    			.header {
    			  color: #000;
    			  background: #BADA55;
    			  width: 960px;
    			  margin: 0 auto;
    			}

    			.footer {
    			 color: #000;
    			 background: #BADA55;
    			  text-align: center;
    			  padding-top: 20px;
    			}

Решение? Вынести повторяющуюся визуальную структуру в миксин

    		.colors-skin {                  /* Использование */
    		  color: #000;                  .footer .colors-skin
    		  background: #BADA55;          .header .colors-skin
    		}

## Объектно-ориентированный CSS
OOCSS

Разделение контейнера и содержимого.
Принцип: внешний вид элемента не зависит от того, где он расположен. Вместо `.my-element button { ... }` создаем отдельный стиль `.control`для конкретного случая
Зачем?

- Все `button` будут выглядеть одинаково
- К любому элементу можно добавить класс `.control`. Работает как mixin
- `.my-element button` не нужно переопределять, если передумали

Страница проекта — Github

## SMACSS — Масштабируемая и
модульная архитектура для CSS

SMACSS расшифровывается как «масштабируемая и модульная архитектура для CSS» (Scalable and Modular Architecture for CSS). Основная цель подхода — уменьшение количества кода и упрощение его поддержки Документация

## БЭМ — Блок, Элемент, Модификатор

## Методология БЭМ — Блок

> Блок в методологии БЭМ — функционально независимый компонент страницы, который может быть повторно использован. В HTML блоки представлены атрибутом `class`

- Название блока характеризует смысл («что это?» — «меню»: menu, «кнопка»: button), а не состояние («какой, как выглядит?» — «красный»: red, «большой»: big)
- В CSS по БЭМ также не рекомендуется использовать селекторы по тегам или id

Страница проекта — Придумано в Яндексе

## Методология БЭМ — Элемент

> Элемент в методологии БЭМ — составная часть блока, которая не может использоваться в отрыве от него

- Название элемента характеризует смысл («что это?» — «пункт»: item, «текст»: text), а не состояние («какой, как выглядит?» — «красный»: red, «большой»: big)
- Структура полного имени элемента соответствует схеме: `имя-блока__имя-элемента`. Имя элемента отделяется от имени блока двумя подчеркиваниями

## Методология БЭМ — Модификатор

> Модификатор в методологии БЭМ — сущность, определяющая внешний вид, состояние или поведение блока либо элемента.

- Название модификатора характеризует внешний вид («размер»: size_s, «тема»: theme_islands), состояние («отключен»: disabled, «фокусированный»: focused) и поведение («направление»: directions_left-top)
- Имя модификатора отделяется от имени блока или элемента одним подчеркиванием

## Форма по БЭМу

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Основной блок

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Блок и его элементы

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Вложенные блоки

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Вложенный блок и его элементы

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Модификаторы

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button">
    		            <span class="button__capture button__capture_red">CLICK ME</span>
    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

Javascript

    		<form class="login-form">
    		    <input type="text"
    		          <input class="text-input login-form__username-input"/>
    		    <div class="login-form__buttons">
    		        <button class="login-form__submit button js-login-button">

    		             <span class="button__capture button__capture_red">CLICK ME</span>

    		        </button>
    		        <button class="login-form__reset login-form__reset_disabled button">
    		            RESET</button>
    		    </div>
    		</form>

## Форма по БЭМу

    		/* блок */
    		.login-form { ... }
    		/* эмененты */
    		.login-form__buttons { ... }
    		.login-form__submit { ... }
    		.login-form__reset { ... }
    		.login-form__username-input { ... }
    		.login-form__password-input { ... }
    		/* модификаторы */
    		.login-form__submit_disabled { ... }
    		.login-form__reset_disabled { ... }

## Форма по БЭМу

    		/* блок */
    		.button { ... }
    		/* элементы */
    		.button__capture { ... }
    		.button__icon { ... }
    		/* модификаторы */
    		.button_big { ... }
    		.button_inverted { ... }
    		.button__capture_red { ... }
    		.button__capture_green { ... }

## Atomic (Functional) CSS

- State of the Web: Atomic CSS
- Tailwind CSS

## Быстрые выводы

- Разрываем связи между css и структурой html через селекторы-классы
- Избавляемся от селекторов зависимых от родителя!
- Придумываем слои: компонент -> приложение -> глобальные стили
- Держим стили внутри своего слоя: `button.html` + `button.css` + `button.js`
- Изолируем компоненты при помощи строгой системы именования
- Расширяем компоненты через глобальные mixin-классы: `.animated, .themed, .hidden, .row`
- Специальные классы и атрибуты: `.js-class, data-mnemo="id"`

Что почитать: Культ карго CSS и Архитектура CSS — Web Стандарты

## CSS-in-JS

## CSS через JS

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

Но зачем???

- CSS гибкий, JS ещё гибче — можем делать всё, что захотим
- Темизация страницы в runtime
- Уменьшение размера загружаемых стилей => ускорение первого рендера
- Не кешируется :(

Реализация — CSS in JS

"CSS в JavaScript: будущее компонентных стилей" — Хабр

## Практика

## Полезные ссылки

- Создание архитектуры программы или как проектировать табуретку — ХАБР
- Шпаргалка по MV-паттернам для проектирования веб-приложений
- Подробнее про роутинг в javascript
- Подробнее про History API и про событие popstate
- Очень подробное описание методологии БЭМ и сайт проекта

## Всем спасибо!