Привет, я Вадим, это мой друг Марат. Мы из Яндекса. Из Карт. Спасибо, что пригласили :)
Мы любим строить сайты. И нам хочется получать от этого удовольствие. Теперь мы получаем радость, потому что придумали Бивис.
У меня есть ребенок. Восемь лет девочке. У кого из вас есть ребенок и он чуть-чуть умеет на html/css и javascript? Принесите ему сегодня ссылку на документацию к БИВИС, и завтра он принесёт вам полноценный сайт, работающий на Node.js
Амбициозно, но заманчиво? Но вы-то не школьники. А кто вы?
Если вы:
-
Разработчик из команды, в которой больше 4-5 человек
-
Разработчик сервиса (сервисов), в котором много похожих блоков или страниц
-
Разработчик общей библитеки блоков
тогда Бивис для вас тоже.
Должны сразу признаться. У нас нет цели заполучить вас в контрибьютеры или принести вам новую веб-религию. Мы не претендуем на мировые лавры. Мы просто с вами по-братски делимся своими придумками.
Ну всё, хватит общих слов, давайте к станку!
##Какие способы разработки сайтов мы знаем?
-
Статический сайт - нагенерить html-страничек, сложить их сервере под управлением Апача, к примеру. Ну и всё.
[смотрим на схему Apache/статический html]
-
Динамический сайт. На том же сервере под управлением Apache работает, к примеру PHP-шаблонизатор. Мы пишем уже не html-странички, а php-странички. В php-файлы мы описываем, как обращаемся в MySQL-базу за данными, а потом там же в php-файле эти данные из базы принимаем и на их основе генерим html-код страницы. А потом Апач отсылает html назад в браузер.
[смотрим на схему Apache/php-шаблонизатор]
Всем известная схема. Ну, разве что, моя дочка про неё ещё не слышала, ей восемь лет, я не торополюсь её знакомить с миром веб-разработки :)
-
Вот эта же схема, кому-то именно она ближе:
[смотрим на схему Lighttpd/xml+xsl/XSLT]
Всё то же самое, только на других технологиях. А у вас это может быть на Python + Django. Ну или Ruby и какие-то серванты.
Ну так мы эту схему и не меняли. Мы заменили в ней составляющие.
[смотрим на схему Node.js/btjson+bt.js/BT]
Вместо Apache у нас Node.js. Вместо php-страничек мы пишем js-файлы, в которых мы обращаемся в серванты за данными, и на основе полученных данных создаём конечный html. Разница лишь в том, что вместо PHP/XSLT-шаблонизаторов мы используем Javascript-шаблонизатор BT, который написали сами.
Возникает вопрос: зачем нужен новый шаблонизатор, если всё уже есть?
Школьнику, который знает чуть-чуть html/css и немного javascript-а, не нужно учить серверный язык программирования - ни PHP, ни Ruby, ни Python. Что ещё учить не надо - Perl, Java, C#, XSL, продолжайте. Я понимаю, что у каждого языка свои преимущества в определённых ситуациях. Но мы и не говорим, что они не нужны. Мы говорим, что типовые задачи веб-разработки можно успешно делать на серверном javascript, который мы уже знаем.
Вот и мы захотели обрести полный контроль над фронтендом. Самое время! Есть все возможности!
Итак, запрос от пользователя пришёл на сервер.
На сервере крутится Node.js
приложение, которое приняло запрос, заглянуло в маршрутизатор и поняло — нужно взять
index.btjson.js
и сгенерить из него html
.
Что такое BTJson?
Это способ описывать страницы. В статических сайтах страница описыватеся чистым html,
в php-сайтах - php-командами, в сайтах на XSLT - описываеся в виде xml, а у нас в виде json. Вот так:
[
{
block: 'head'
},
{
block: 'authorization',
actionUrl: '/?task=login'
}
]
В какой-то такой html он превращается на лету:
<div class="head">
...
</div>
<form class="authorization" action="/?task=login">
...
</form>
Там где три точки, там весь остальной html, нужный для устройства шапки или формы логина.
Мы больше не пишем html-теги, потому что в Яндексе мы давно научились смотреть на веб-страницу, как на набор блоков. Каждый выполняет свою простую функцию, вместе - это страница.
Вы, скорее всего, тоже разбиваете страницу на небольшие части, и называете их про себя как-то: кто модулями, кто ещё как. Мы называем блоками, потому что мы из Яндекса, а там родилась концепция верстки, в которой впервые прозвучало слово "блок". :)
Вы помните такую концепцию?
2006 год. Родилась концепция АНБ. Это были рекомендации по оформлению кода в проекте.
2009-210 года. Родился БЭМ. Фреймворк для разработки сайтов. Это реализация АНБ, которую насытили дополнительными идеями, которых не было ещё в 2006 году.
2013 год. Мы придумали Бивис. Это тоже фреймворк для разработки сайтов. Фактически, это не что иное, как альтернативная реализация АНБ, сделанная после БЭМа. Мы не согласны с реализацией АНБ командой БЕМ-разработчиков, и реализовали фреймфорк, который, как нам кажется, реализует первоначальные принципы АНБ — настоящую абсолютную независимость блоков.
Это некая логическая единица веб-страницы. Абстрактная штука. Давайте будем считать, что набор тегов, которые формируют структуру формы авторизации, это и есть некий блок. Назовём его authorization. Внутри него есть некие теги, без которых форма не сможет выполнять свою функцию - принимать данные от пользователя и откправлять их на сервер - текстовый инпут и сабмит-кнопка. Эти теги бессмысленны без формы, поэтому их можно считать элементами блока. Такая вот абстракция.
А вокруг этой абcтракции придумали немного правил для облегчения разработки.
- Для стилизации блоков не используем id, а только css-классы.
- Чтобы исключить наложение стилей элементов от разных блоков, вложенных друг в друга, классы для элементов обязательно
включают в себя имя блока
<div class="page"> <h1 class="page__title"></h1> <div class="article"> <h2 class="article__title"></h2> </div> </div>
- Чтобы в
html/css
сэмулироватьнаследование и полиморфизм
, придуманы модификаторы. Например, на странице две кнопки, но одна синяя, другая зеленая. Пишем стили для синей кнопки, а для того, чтобы сделать вторую зеленую, используем синюю, но модифицируем дополнительным классом, в котором только одно css-свойство:<style> .button { /* все стили для синей кнопки */ } .button_green { background: green; } </style> <div class="button">синяя кнопка</div> <div class="button button_green">зеленая</div>
Тогда, в 2006 году, это были только рекомендации, не было никаких инструментов, не было фреймфорков, мы писали код тегами. Но сейчас есть Бивис, который понять легко, а использовать ещё легче :)
Это важно. Описание страницы должно быть масикмально простым. Чем предмет проще, тем легче им пользоваться. Бабушка моей жены любит печь хлеб сама. Мы подарили ей хлебопечку. Казалось бы - насыпь муки, дрожжей, налей воды и масла и нажми пару кнопок - очень просто. Но бабушка Дуся не пользуется, потому что не может запомнить в какой последовательности какие кнопки нажимать - из инструкции старому человеку это непонятно. Пользуется газовой духовкой - куда проще - поджечь комфорку, сунуть противень внутрь, закрыть дверцу.
Мы хотим получать удовольствие от верстки, а не боль. Поэтому описание блоков на странице обязано быть таким, чтобы не приходилось изучать талмуды, чтобы понять, что блок делает.
Вот такие описания блоков.
[
{
block: 'head'
},
{
block: 'authorization',
actionUrl: '/?task=login'
}
]
Смотрите, здесь не описано ни одного элемента, ни одного тега и атрибута. Но из каждой декларации сгенерится развесистый html, например это может быть такой:
<div class="head">
<a class="head__logo" href="/"><img src="logo.png" /></a>
<h1 class="head__title">Демо-страница</h1>
<a class="head__rss" href="/?rss"><img src="rss.png" /></a>
</div>
<form class="authorization" action="/?task=login">
<input class="authorization__login" value=""/>
<button class="authorization__submit"/>
</form>
Или даже больше. Но я не хочу думать про такой html. Я хочу мыслить простыми предметами - блоками. А что о блоке мне нужно знать, чтобы позвать его на страницу? Да только имя. И это так по человечески. Ведь, когда я хочу позвать к себе в гости Марата, я делаю этоочень просто - я зову его по имени и говорю: "Марат, а приходи к нам в гости сегодня, жена яблочный пирог испечёт, посидим". А у Марата жена и двое детей, к примеру. Я же не зову его отдельно, отдельно его жену и каждого ребенка в отдельности. Нет, только его, а уж он сам приведеёт всё семейсвто, будьте уверены.
{
друг: 'Марат'
}
И с блоком так же. Я НЕ ХОЧУ думать, о том, как он внутри устроен, из каких элементов. Я зову его просто по имени. Ну и если надо сообщаю блоку какую-то дополнительную информацию. Командир в окопе кричит солдату: "Рядовой Петренко, прикрыть правый фланг!".
{
рядовой: 'Петренко',
стрелять: '/?туда'
}
Всё! Командир приказал — Петренко стреляет, куда сказали. И пусть Петренко сам решает какой рукой стрелять - левой или правой, каким пальцем на спусковой крючок жать - указательным или ещё каким. Командиру какое дело до этих деталй вообще?
Ну что, похоже на правду?
[
{
block: 'head'
},
{
block: 'authorization',
actionUrl: '/?task=login'
}
]
Я, как командир веб-страницы, не хочу думать о такой чепухе, как палец солдата. Пусть об этом думает сам солдат. Это его личное дело, приватное. Пусть блок сам решает, в какой html он должен превратиться, чтобы лучшим образом выполнить свою задачу. Как он устроен внутри, я как пользователь блока, думать не хочу.
Ну, в самом деле, я когда в ресторан прихожу, и заказываю солянку, я же не даю указаний официанту: "Так, дорогуша, нагрей суп в каструле, потом налей два половника в глубокую тарелку, насыпь сверху зелени и сметаны ложку и только после этого неси мне." Нет, я просто говорю: "Мне солянку". Я точно знаю, что в кафе солянка уже сварена, её сейчас мне подадут.
И с шапкой так же: "Мне шапку!"
{
block: 'head'
}
Если блок уже разработан кем-то по правилам Бивиса, например в какой-то общей библиотеке, вам хорошо — позвали блок и voila — сегенерировался нужный html блока.
Но это когда вы пользуетесь готовой библиотекой блоков и в рестораны обедать ходите.
Другое дело, когда я дома говорю жене: "Хочу на ужин борщ с фасолью и сметаной". Знаете, что мне жена отвечает? "Легко! Картошку чисть..."
Улавливаете? Если вы создаёте новый блок, вам придётся предватительно самому и картошку начистить и фасоль вымочить, и мясо кубиками нарезать. То есть сварить борщ по некоторым правилам. И только потом вы сможете щелкнуть пальцами и борщ окажется у вас в тарелке.
Чтобы btjson превратился/трансформировался в html, вам нужно написать правила для трансформации. На XSLT писали? XML в HTML трансформировали? Та же идея: пишем шаблоны. Всё, как в XSL.
А есть кто не писал на XSL? Вы тоже сейчас поймёте.
Когда браузер запрашивает index.html, Node.js сервер обрабатывает ваш запрос и находит ваш btjson
{
block: 'head'
}
И передаёт управление специальному шаблонизатору, который называется BT. Всё как с PHP, схема та же. У BT одна простая задача — превратить btjson в html.
[смотрим на схему Node.js/btjson+bt.js/BT]
BT
- это и есть повар в ресторане или ваша жена на кухне. Именно BT
готовит внутренности блока,
то есть формирует нужный html.
bt.match('head', function (ctx) {
ctx.setTag('div');
});
Ваше описание блока приходит в функцию bt.match
в виде переменной ctx
. Эта функция физически находится в
файле index.bt.js
.
[смотрим на схему Node.js/btjson+bt.js/BT]
Конкретно этот шаблон сегенерит простой html
<div class="head"></div>
BT — это очень простой декларативный шаблонизатор. Он берет json и "накладывает" на него правила. Это как пазлы - совпали предмет и отверстие - опа - картинка сложилась. Это как XSLT. Это как в CSS.
h1 {
color: red;
}
Что это? Даже школьник ответит: "Это CSS-селектор". Ну тогда поздравляем, вы умеете писать декларативные шаблоны.
CSS-селекторы - это декларативные шаблоны. Заголовок станет красным только, если он есть на странице. Если h1 на странице нет, то и красить нечего, правда?
В этом вся соль декларативных языков программирования. Вместо явных инструкций (как в PHP) - только матчеры, которые применяются к данным, если они есть. Матчатся или не матчатся.
Вот шаблон на BT.
bt.match('head', function (ctx) {
ctx.setTag('div');
});
Он записывается в виде функции match
, в которую мы передаём имя блока. Очень похоже на CSS.
А вторым аргументом пишем функцию, внутрь которой передаём исходный btjson в переменной ctx
Давайте усложним. Я хочу, чтобы внутри шапки появился заголовок.
Я добавил комментарии в код, чтобы вы всё поняли, а я помолчу.
// регистрируем матчер для блока head
bt.match('head', function (ctx) {
// устанавливаем HTML тег для самой шапки
ctx.setTag('div');
// Генерим внутри шапки новый btjson и устанавливаем его, как содержимое шапки
ctx.setContent({
elem: 'title', // указываем элемент title в качестве содержимого
text: 'CodeFest 2014' // произвольный текст для заголовка
});
});
// регистрируем матчер для элемента title блока head
// внутри ctx уже json, переданный в родительском ctx.setContent()
bt.match('head__title', function (ctx) {
// выставляем тег h1 для элемента title
ctx.setTag('h1');
// Получаем параметр text
var text = ctx.getParam('text');
// задаём содержимое заголовку
ctx.setContent(text);
});
Результат:
<div class="head">
<h1 class="head__title">CodeFest 2014</h1>
</div>
А теперь сделаем шапку гибкой - чтобы можно было задавать текст заголовка на жестко, а гибко.
// регистрируем матчер для блока head
bt.match('head', function (ctx) {
ctx.setTag('div');
ctx.setContent({
elem: 'title',
text: ctx.getParam('text') // получаем значение из параметра
});
});
bt.match('head__title', function (ctx) {
ctx.setTag('h1');
var text = ctx.getParam('text');
ctx.setContent(text);
});
И тогда в btjson можно передать опцию/параметр
{
block: 'head',
text: 'CodeFest 2014, Новосибирск'
}
Наш результат:
<div class="head">
<h1 class="head__title">CodeFest 2014, Новосибирск</h1>
</div>
А вот посмотрите на реальный пример их Яндекса. Это btjson-описание шапки.
{
"block": "y-header",
"view": "islet-search",
"showSearch": true,
"searchAction": "/",
"searchQuery": "Текст запроса",
"showUser": true,
"userPic": "user.jpg",
"userLogin": "FenekFox"
}