Компания
1 159,06
рейтинг
5 апреля 2012 в 14:10

Разработка → JavaScript на сервере, 1ms на трансформацию

Зачем?



Вопрос “Зачем?” — самый главный при принятии любого решения. В нашем случае причин было несколько.

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

Вообще это частая и, на мой взгляд, не очень хорошая практика написания инструментов для верстальщиков. Понятно, что им нужны инструменты, но реализуют эти инструменты люди, которые весьма отдаленно себе представляют ежедневные задачи верстальщиков. Скорее наоборот, часто принимаются решения плана «дадим им писать условия и циклы, а больше на верстке ничего понадобится не может». Возможно, это вина самих верстальщиков и их квалификации.

Но в Mail.Ru Group есть целая команда высококвалифицированных людей, знающих JS, способных самостоятельно написать инструмент, а самое главное — они же им и будут пользоваться.

Во-вторых, задачи. Возьмем проект Почта@Mail.ru. Мы не можем отказаться от шаблонизации на сервере – нам нужна быстрая загрузка при первом входе. Мы не можем отказаться от шаблонизации на клиенте – люди должны видеть высокую скорость реакции на их действия, а значит, обязателен AJAX и шаблонизация на клиенте.

Проблема очевидна: два набора совершенно разных шаблонов на сервере и на клиенте. А самое обидное, что решают они одну и ту же задачу. Дублирование логики нас просто измотало.

v8 — это интерпретатор JavaScript, а значит, мы можем получить один шаблон, который работает как на сервере, так и на клиенте.

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

Что нужно



Скажу сразу, мы очень сильно ограничены по серверному времени на трансформацию, поэтому реализовывать нечто очень функциональное возможности не было. Однако и оставлять старый функционал с единственной разницей в добавлении прослойки из v8 — странная идея.

Я давно и много использую для трансформации XSLT (не самый быстрый вариант трансформации), хотя, если его правильно использовать, он показывает хорошие цифры (я говорю про libxslt). Но у XSLT есть очень мощный инструмент шаблонизации – переопределение шаблонов. Мы решили реализовать нечто похожее, но намного проще.

/head.xml
	…
	<title><fest:get name=”title”/></title>
	<fest:set name=”title”>Mail.ru</fest:set>
/mail.xml
	…
	<fest:include src=”head.xml”/>
	<fest:set name=”title”>Почта Mail.ru</fest:set>


Странно было бы использовать v8 и не дать в шаблонах доступ к JavaScript.

<fest:script>
	var text = “mail.ru”
</fest:script>
<fest:value>text</fest:value>


И еще много чего по мелочи, помимо стандартных условий и циклов.

XML



В качестве синтаксиса для шаблонизатора мы взяли XML.

Поддержка базового функционала в IDE. Сто процентов популярных IDE знают, что такое XML. Расстановку переносов, подсветку, базовое автодополнение вы получаете бесплатно.

Валидация на уровне IDE. Валидный HTML получается за счет валидных шаблонов. Опять же, все IDE умеют валидировать xml как таковой.

Модульность из коробки (name spaces). Базовые возможности шаблонизатора очень быстро захочется расширить. Например, добавить тег, который позволяет делать проекты на нескольких языках. Система Name Spaces позволяет легко это сделать.

Широкий набор готовых инструментов. За много лет скопилось множество инструментов для обработки XML, например, библиотеки по обработке XML посредством XSL или целый класс SAX парсеров, XSD и DTD схемы для валидации и т.п.

Совпадение синтаксиса обрабатывающих конструкций и результирующих конструкций. Другими словами, создавать XML на XML удобно, fest-шаблоны легко читать. Плюс решены все вопросы экранирования данных.

Реализация





Ковбойство. Я сам недавно узнал, что есть такой термин.

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

Отличие этой задачи от типичной: был известен шаблон и результирующий HTML, который этот шаблон должен выдавать. Мы с Костей (Frontend разработчик почты) начали писать свои реализации. Раз в неделю мы сравнивали замеры скорости трансформации.

Мы выбрали два разных подхода: он компилировал шаблон в функцию, а я — в структуру. Пример самой простой структуры:

01	[
02		{action:"template"},
03		"<html>...",
04		{action:"value"},
05		"json.value"
06	]


Вторая строка означает начало шаблона. Третью надо просто отдать браузеру. Четвертая говорит, что пятую надо исполнить как JavaScript и результат исполнения отдать браузеру.

01	[	
02		{action:"template"},
03		"<html>....”,
04		{action:"if"},
05		"json.value",
06		"<span>true</span>",
07		"<span>false</span>"
08	]


Вариант немного сложнее. Четвертая строчка означает, что пятую надо исполнить и если результат истина или ложь, то отдать шестую или седьмую строку соответственно.

Вариант с функцией особо пояснять не надо.

01	function template(json){
02		var html = "";
03		html += "<html>…";
04		html += json.value;
05		return html;
07	}


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

Чтобы понимать, что дал такой подход: моя первая реализация выполняла задачу за 200ms. Когда мы выжали все, что можно, а потом соединили лучшее из двух наших программ, то получили 3ms.

Если описать текущую реализацию кратко, то мы циклы транслируем в циклы, условные операторы в условные операторы и т.д.

fest:foreach			for(i = 0; i < l; i++) {}
fest:if				if(value) {}
fest:choose			if(value) {} else {}


Нет сужений контекста. Да, это ограничение, но зато нет накладных расходов на ограничение контекста, а самое главное, что, как только сужаешь контекст, сразу возникает задача достать что-то из глобального контекста или из контекста уровнем выше.

Важно, что шаблоны транслируются в JS-функцию, которая работает в режиме strict mode. Это не дает верстальщикам шансов написать код, который приведет к утечкам памяти.

Везде, где нужна логическая работа с данными, доступен JavaScript.

<fest:if test=”_javascript_”></fest:if>
<fest:value>_javascript_</fest:value>


Все конструкции, которые предполагают исполнение JavaScript, оборачиваются в try catch.

Все конструкции, которые предполагают вывод в HTML после выполнения JavaScript, по умолчанию проходят HTML escape.

<fest:value>json.name</fest:value>

try {
	html += __escape(json.name)
} catch(e) {}


С самого начала разработка шаблонизатора ведется в открытом виде.
https://github.com/mailru/fest

Возможности интеграции



С одной стороны, v8 — это только библиотека, которая позволяет интерпретировать JavaScript. Сама по себе она кажется бесполезной – никакого доступа к системе. Но, с другой стороны, она легко прикручивается к другим языкам.

Имея нулевой опыт программирования на Cи и Perl, я сделал тестовые примеры на обоих языках. Плюс на текущий момент у нас есть связка с Python.

Ну и, конечно, NodeJS для прототипов и браузеры — среды, где JavaScript шаблоны работают из коробки.

Условия, близкие к боевым



Получив 3ms, я пошел к сервер-сайд программистам. На вопрос, сколько у меня есть времени на запрос, который отдает список писем пользователя, они сказали: не больше 4ms. У меня уже было 3ms на трансформацию, надо было пробовать.

Список писем у нас отдает наш собственный http-сервер, написанный на Cи. Получение данных — операции, которые не конкурируют за процессор, поэтому их не замеряли. Остановились на подготовке данных к трансформации и на самой трансформации.

По историческим причинам наш http сервер хранит данные в плоском хеше.

msg_length = 5
msg_1_title = “letter”
msg_1_Unread = 1


Так как мы говорим о JavaScript, то первое, что приходит на ум, — это JSON

msg = [ {title: “letter”, Unread: true} ]


Мы взяли строку с плоским хешом, поместили ее в память и стали добиваться результата, когда при трансформации шаблона в v8 JavaScript оперировал с JSON.

Вариантов перебрали много. Пробрасывать объект, пробрасывать строку и парсить ее на JavaScript, пробрасывать строку и пропускать ее через JSON.parse.

Как ни странно, самым быстрым оказалось преобразовать плоский хеш в строку, которая совпадает с JSON, и в v8 отдать строку

“template([ {title: \“letter\”, Unread: true} ])”


Но, несмотря на все, мы уперлись в 6ms при трансформации 2ms. Все были готовы сдаться. Я все же решил взять исходные данные, строку с плоским хешом и, используя тот же скомпилированный шаблон, получить нужный HTML на NodeJS.

Получил 4ms. Когда пришел с этой цифрой к нашим сишникам, честно говоря, ожидал фразы “Классно, но NodeJS писать у нас нет ресурсов” Но вместо этого услышал “Если NodeJS может за 4ms, значит мы тоже сможем!”.



Именно в этот момент я понял — мы доведем это до продакшена. Появилось второе дыхание!

Решение оказалось простым. Раз мы 67% времени теряем на подготовке данных, а данные в принципе у нас уже есть, надо выкинуть подготовку данных.

Мы пробросили в v8 фукцию __get(‘key’). Таким образом, мы из v8 забирали данные напрямую из хеша нашего http сервера. Нет конвертации данных в нужный формат. Нет преобразования этой строки в объект внутри v8. Мы вышли на 3ms и имели запас 1ms.



Почти продакшен



Итак, выглядит все замечательно, но мы еще и близко не были на продакшене. Чешутся руки попробовать.

Берем отдельный сервер, поднимаем на нем версию http сервера, который работает с v8, и дублируем реальные запросы на него. Оставляем на 30 часов одно ядро 2.2 ГГц Xeon.

10 000 000+ хитов
1.6ms среднее время трансформации

992 422		10% между	2 и 5ms
208 464		2% между	5 и 10ms
39 649		0,4%		больше 10ms


Только 12% были больше 2ms. v8 стабильно ведет себя по памяти.

Продакшен



Я пришел с последними цифрами к заместителю технического директора, сказав, что v8 готов к продакшену, надо сделать отдельный небольшой проект, который, если что, можно и забыть в случае неудачи. В ответ получил «цифры хорошие, отдельный проект, провал которого не страшен, — это правильно, но ты правда хочешь запустить v8? Начни с главной страницы Mail.Ru». Вопрос поставлен правильно – либо мы делаем дело, либо развлекаемся в сторонке.

Верстка главной страницы на fest заняла три дня. Выключили один сервер из балансера, залили туда версию с v8 и продублировали запросы. Все выкладки будут происходить в контексте одного демона/ядра.

Дальше я расскажу очень поучительную историю с хорошим концом.

Всегда детально выясняйте, что показывают ваши графики. Мы пустили на тестовый сервер половину нагрузки. Потребление процессора было в три раза выше обычного. Выглядело как провал, проигрыш по ресурсам в шесть раз по сравнению с текущим шаблонизатором.

Стали смотреть. Тут я немного расскажу про архитектуру главной. На ней собирается информация от разных проектов. Собирает ее внутренняя разработка, мы ее называем RB. Из 165кб, которые генерируются для отдачи главной, 100кб собирает RB. И происходит следующее: RB отдает куски HTML через http сервер в v8, v8 конкатенирует их со своими строками, а результат возвращает все это обратно в http-сервер.

Налицо двойной проброс данных. Сделали оптимизацию. Теперь v8 вместо построения одной большой строки, включающей в себя данные от RB, отдает данные сразу в http-сервер.

__push_string(‘foo’);
__push_rb(id);
__push_string(‘bar’);


Как плюс, нет конкатенации строк на v8, нет двойного проброса RB из сервера в v8 и обратно, а самое главное, любой проброс данных — это конвертация из utf-8 в utf-16 и обратно. V8 все хранит в utf-16.

Был профит, ресурсы потреблялись в два раза больше, чем обычно, а не в три. Т.е. мы все еще проигрывали в четыре раза, хотя вроде бы выжали все до капли.

А теперь поучительная часть. Я интереса ради взял нагрузку, которую мы тестировали, умножил на два, на количество демонов на машине и на количество машин. Получил 440 000 000 хитов. При этом у нас в сутки 110 000 000 хитов. Закрались смутные сомнения.

Пошли смотреть логии. Оказалось, что на каждый запрос с нагрузкой мы получали три запроса с отчетами в логи для статистики! Реальная нагрузка на один http-сервер в четыре раза ниже той, на которой тестируемся мы!

На следующее утро мы раскатили версию главной страницы с v8.

Данные на сегодня:
Размер отдаваемого HTML, который генерирует v8 65кб.
Время, работы v8 на запрос 1ms.
В среднем v8 требует 40MB на контекст.

Пара уточнений



Все, кто думают про v8, натыкались на статью Игоря Сысоева sysoev.ru/prog/v8.html

За все время работы над этой задачей нам очень сильно помогал разработчик v8 Вячеслав Егоров (http://mrale.ph).

Опасения про память оправданны. Если для вас критична корректная работа (при условии, что нехватка памяти — это штатная ситуация), то у вас будут проблемы. Корректно перехватить ошибку аллокации можно, но все, что можно сделать по этому поводу, — это корректно перезапуститься.

Но скажу честно, у нас есть только один продукт, где это критично. Что касается главной страницы — у нас многократный запас по памяти и мы его очень хорошо мониторим.

Оказалось, что у нас v8 trunk течет. Вячеславу воспроизвести это не удалось, но, я думаю, мы соберем тестовый пример, который поможет разработчикам найти утечку. Версия 3.6 ведет себя по памяти прекрасно.

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


github.com/mailru/fest шаблонизатор
code.google.com/p/v8 v8 API
sysoev.ru/prog/v8.html статья Игоря Сысоева


Андрей Сумин, руководитель разработки клиентской части Mail.ru
Автор: @AndrewSumin

Комментарии (147)

  • +10
    Прекрасная статья.
    А выложить в public ваш чудесный шаблонизатор не хотите? ;)
    • +13
    • +7
      На него есть ссылка в статье, но добвил блок в конце статьи.
  • 0
    > Возьмем проект Почта@Mail.ru. Мы не можем отказаться от шаблонизации на сервере – нам нужна быстрая загрузка при первом входе.

    Странно, почему-то Gmail может, а вы не можете.

    • +10
      У Gmail прогресбар «почта загружается».

      Но, допустим мы решаем что это приемлимо, всеравно будут блоки с шаблонизацией на сервере.
      • +1
        Понятно. Удивление как раз вызвал аргумент «нам нужна быстрая загрузка при первом входе».
      • 0
        Кстати, мне тут Миша Сабуренков подсказал плагинчик, который показывает почту в виде простенькой таблички до того как GMail прогрузился. Инженерное решение, блин.
    • +2
      даже если откажутся, все равно будут поддерживать noscript версию, у гмэйла же есть
      все таки почта это не развлекательный ресурс и требовать от человека включенный JS для использования основного функционала — написания и чтения писем — не очнеь хорошо. Хоть и выключенный JS бывает крайне редко
      • +10
        Думаю, в наше время, требовать включенный js для чего угодно вполне нормально
    • НЛО прилетело и опубликовало эту надпись здесь
      • +2
        Веб-интерфейс в браузере, клиентская часть? Субъективно, кажется, что так и есть.
      • +1
        Загружается gmail дольше — факт.

        А работает оно также, это с учетом того что пинг до mail.ru всяко меньше.
      • +2
        Потому что загружает сразу много данных. Если обратите внимание, письма на первой странице открываются сразу без дополнительных запросов.

        Просто у Gmail немного иной подход, который позволяет вдобавок работать в офлайне.
  • +1
    Занятно, только увы не реюзабельно, ведь всё завязано на вашу архитектуру и окружение.
    • +4
      Ничуть, готовый шаблон это файл с js функцией.
      Это достаточно легко встраивается в любую архитектуру.

      Но, тем не менее, основной поинт что JavaScript на сервере это уже сегодня, и мы тоже это проверили на себе.
      • +2
        Удивительная фраза «JavaScript на сервере это уже сегодня». JS на сервере — это даже не вчера, это позавчера. Был ASP (где были JScript и VBScript на сервере), был веб-сервер у Нетскейпа, где был серверный JS.
        • +2
          Было, я начинал с проекта aptana jaxer.
          Но было не значит работало.
          • –1
            А я на ASP и сайты делал. Работало.
    • 0
      Да, это суровая правда. Редко универсальные решения бывают очень эффективными, сносным, не более. Как правило эффективное решение разрабатывается для частных случаев.
  • +5
    Отличная статья! Не так часто можно посмотреть кухню больших компаний, а ведь кто еще сможет рассказать об опыте работы с по-настоящему большими нагрузками.
  • +29
    Последнее время Mail.ru приятно удивляет. И разработчиков думающих набирают.
    Что-то же должно меняться к лучшему.
    • 0
      Когда б они уже к этой красоте сделали бы доступ по https…
      • +2
        Скоро сделаем. Потерпите немного.
        • 0
          Никак не мог понять, что вам до этого-то всё мешало SSL запустить? Ну не нехватка же ресурсов?!
          • +8
            Ничего не мешало. Надо было просто за нее взяться. Запуск SSL — это гигантская по объему техническая задача. Нужно перелопатить всю верстку, в поисках httpшных картиной и js'ов, заменив их на schemeless (чтобы браузеры не ругались). Нужно корректно выкусить из серверной части все темные углы, где такое закардкожено. Нужно разработать прокси-сервер для показа сторонних картинок в письмах. Нужно научить показывать рекламу и баннеры по https, и автоматически и интеллектуально защитить админку рекламной сети от заноса туда баннеров, на которые браузер будет ругаться на httpsной версии. Нужно оценить рост нагрузки на сервера (поддерджка SSL — это такая задача для процессора, что на нем можно жарить яичницу). Нужно в конце-концов решить организационные задачи, типа доработки тест-планов у тестировщиков. И т.д., и т.п. И это все на фоне новой разработки, запуска фич, без ущерба для старого и только что созданного функционала.
            • 0
              Я предполагал по наивности, что достаточно поставить https-фронтенды и кроме большой нагрузки на процессор, ничего остального даже не придётся трогать.
              • 0
                К сожалению, httpsные фронтенды не заменят по волшебству httpшные картинки в верстке.
  • 0
    Я правильно понял, что для клиентской шаблонизации на клиент уходят уже скомпилированные шаблоны?
    • 0
      Скорее так, не скомпилированные шаблоны есть только в девелопменте. Дальше все и везде скомпилированое
  • –5
    Привет, Рубаха!
  • –1
    Напоминает меня.
    Уже давно разрабатываю свой шаблонизатор под NodeJS. Для меня сразу было очевидно, что управляющие конструкции должны быть в формате XML.
    Сначала шаблон компилировался в структуру. Когда же была готова стабильная версия, решил сравнить производительность с Dust. Оказалось, что такая архитектура шаблонизатора изначально тупиковая — сказываются накладные расходы при обработке полученной структуры.
    Сейчас же допиливается версия компиляции в функцию и уже выигрывает у Dust в 3-5 раз.

    Ну и плоский хеш это, конечно, жестоко.
    • –1
      Dust, по вашей ссылке — давно заброшенный проект. Интересно сравнение весрии LinkedIn-a — github.com/linkedin/dustjs, с вашим шаблонизатором.
      • –1
        Спасибо, а то я думаю что же они не убирают require.paths.unshift из кода…
    • +8
      Для меня сразу было очевидно, что управляющие конструкции должны быть в формате XML.

      Странная очевидность. Лично для меня очевидно, что это плохое решение. Если нам надо значение вставить в параметр, то вся структура xml рушится:
      <a href="/user/<fest:value>json.user.name</fest:value>">Go to <fest:value>json.user.name</fest:value> profile</a>
      


      Да и смотрится оно просто блевотно в сравнении с, например
      <a href="/user/{json.user.name}">Go to {json.user.name} profile</a>
      
      • +6
        Фигурные скобки в аттрибутах мы поддерживаем.
        А рассуждать понятиями блевотно/не блевотно очень странно для технического человека.
        Понятия поддерживаемость, интеграция, валидация куда ближе.
        • +16
          На самом деле рассуждать так совершенно не странно. Программирование — это не только тяжёлая рутинная работа, но ещё и творчество и удовольствие, потому люди выбирают не только инструменты «лучше», но и инструменты «приятнее».

          Есть такое понятие, как усталость от кода. Чем больше усталость — тем меньше КПД программиста. Чем меньше КПД программиста — тем хуже «поддерживаемость, интеграция, валидация».

          А усталость — вещь субъективная, как и «блевотно/не блевотно».

          Такая позицитая достаточно аргументирована для технического человека?
          • +2
            Такая достаточно.
          • +1
            Еще люди выбирают инструмены у которых свойства «проще прочитать», «быстрее написать» и «интуитивней понять» максимальны.
            Для меня fest это ЯП на XML, который транслируется в JavaScript. XML транслируется в JavaScript… ок — сейчас что только в JavaScript ни транслируется.

            «проще прочитать» — получает минус (XML читается хуже)
            «быстрее написать» — получает минус (XML)
            «интуитивней понять» — на уровне
            и как бонус микс декларативности и функциональности…
            <fest:script>
                var text = "mail.ru"
            </fest:script>
            <fest:value>text</fest:value>


            Если бы мне хотелось добавить строгости и ограничить возможности я бы постарался написать некий диалект JavaScript, который будет понятен и разработчикам и верстальщикам. И транслировался бы он в такой же шустрый код.
            <ul>
            items.forEach((v, i, all) =>
                <li>all[i]</li>
            )
            items.forEach(v =>
                <li>v</li>
            )
            </ul>
      • 0
        Об этом я писал ниже.
      • 0
        Если нам надо значение вставить в параметр, то вся структура xml рушится:
        <a href="/user/<fest:value>json.user.name</fest:value>">Go to <fest:value>json.user.name</fest:value> profile</a>
        


        Там все несколько сложнее, почти как в xsl:
        <a>
          <fest:attributes>
            <fest:attribute name="href">/user/<fest:value>json.user.name</fest:value></fest:attribute>
          </fest:attributes>
          Go to <fest:value>json.user.name</fest:value> 
        </a>
        
        • 0
          О ужас! Мои глаза! И кто может посчитать ЭТО хорошим решением?
          • 0
            Пока нет в документации, но можно делать так
            <a href="/user/{json.user.name}">Go to <fest:value>json.user.name</fest:value></a>
            


            Нет в документации, потому что сами тестируем.
  • +3
    Мне в этом вопросе, больше понравился подход LinkedIn-a — они вместо написания своего велосипеда, проанализировали уже готовые решения и выбрали подходящее им. Подробнее в их блоге — engineering.linkedin.com/frontend/client-side-templating-throwdown-mustache-handlebars-dustjs-and-more
    • 0
      Интересная ссылка, спасибо.
  • +1
    Тоже сейчас занимаюсь реализацией шаблонизатора, который умеет собираться на сервере и на клиенте
    Серверная часть компилится в PHP — код, клиентская в Js — код.
    Примечательно, что Js шаблон реализован точно так же в виде функции, которая возвращяет HTML и использует для генерации нативный js.

    По поводу шаблона в виде XML: я в своё время отказался от это идеии ввиду невозможности написать так

    <div class="bla {if $active} active{/if}">
    


    Как видно, я остановился на синткасисе а-ля Smarty. К тому же во всех нормальных IDE всё прекрасно подсвечивается и подсказывается.
    • 0
      Если немного подумать, то могут быть и другие решения, например как в fest с тегом attribute. Но мне такое решение не по душе, я сделал так:

      <tsn:var name="className" expr="active ? 'active' : ''" />
      


      А в атрибут уже выводится так:
      <div class="bla &tsn.echo.className;" />
      
      • 0
        В любом случае синтаксис Smarty выглядит более компактным и удобным.
        • 0
          А мне синтаксис XML кажется более «говорящим».
          • +8
            А мне синтаксис XML кажется более «говорящим».

            Согласен! И он говорит: «Нет, не ломай себе глаза, человек, который меня придумал — просто издевается над тобой! Иди и убей его!»
            • +2
              По вашему верстальщикам нужно доплачивать за «особые условия работы»?
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Да, там стандартное экранирование.
      Но если нужна прям логика то можно ее обернуть в <fest:script/>
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Проблемы компиляции.

              <input type="checkbox">
                  <fest:attribute name="class">cbx</div>
                  <fest:if test="false">
                      <fest:attribute name="checked">checked</fest:attribute>
                  </fest:if>
              </input>
          


          Мы должны в скомпилированном коде написать

          html += '<input type="checkbox"';
          html += ' class="cbx"';
          fest_if = node.attributes.test
          if (fest_if){
              html += ' checked="checked"';
          }
          


          А теперь вопрос, в каком месте надо написать
          html += '/>';
          

          Как понять что секция с атрибутами закончилась и можно закрыть тег?
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              так вот и начался fest:if а закрывать рано.
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  Возможно, но что делать, когда нужно fest:choose использовать?
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Если для вас незакртые теги стандартная ситуация, не используте fest.
          Это возможно но лучше поискать другой инструмент, мы не претендуем на универсальность.
          • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Пока не планируем.
      Она очень сильно зависит от реализации проекта.
      Плюс вы можете сделать JS функцию lang('key') и прекрасно с этим жить.
      • +1
        Как вариант — запилить отдельный тег для этого. Такое я встречал в одном Java-шаблонизаторе.
  • +2
    У нас тоже в свое время встал вопрос о шаблонизации.
    Ситуация усугублялась тем, что бизнес логика была реализована на php, а кусочек ее — демон — на Ноде.

    В качестве шаблонизатора на пхп части мы использовали толковую штуку Twig, которая является реализацией Джанговских шаблонов.

    Перебирая возможные движки для Ноды, с удивлением наткнулись на SWIG, который, за исключением некоторых мелочей, умел все, что нужно.

    Теперь изучаем, как серверный SWIG плавно перенести в браузер.

    • 0
      Мы у себя используем SWIG для трансформации на ноде и в браузере. Далее поток мыслей — январь 2012 года (в феврале его обновили хорошо, но я не смотрел, что там сделали). В браузер перенес небольшим допиливанием. Ужасно объемный код на выходе, который получает в итоге клиент. Из-за того что, все конструкции заворачиваются в замыкания, то нет возможности установить переменную внутри конструкции if, а затем к ней обратиться извне. В Opera проблемы из-за того, что в скомпилированном шаблоне все обращения к переменным проходят в итоге через window, а Opera создает в window переменные указатели на input с именами аттрибутов name и получаем строки [Object HTMLInputElement] в выдаче.
    • –1
      Вроде как twigjs более точно повторяет оригинальный Twig. И нормально работает в браузере.
      • –1
        Посмотрел Вашу ссылку — последние изменения датированы 7 месяцами назад и npm не находит проект в репозитории (намекает на несовместимость с актуальными версиями Ноды).

        Вы его использовали где-то в работе? Каковы ощущения, если да?
        • –1
          Я своими немногими местами не везде перелез на 0.6. Может гляну, настолько ли они несовместимы, судя по всему влияет ограничение в package.json и всё.

          Когда я смотрел Swig там не хватало некоторых конструкций/автопеременных по части циклов, так что не удалось его сразу начать применять.
  • –7
    Так в каком месте у вас 1ms?
    Главная mail.ru вижу сейчас собирается за 67ms.
    • +3
      Не путайте собирается и доставляется браузеру.
      Вы же не думаете что данные по сети мгновенно приходят.
      Плюс я пишу именно про шаблонизацию, а именно превращение данных в HTML.
      • 0
        Доходят он за несколько ms согласно firebug. Возможно ещё несколько ms тратится на сжатие.
        • +2
          На сжатие тратится 10ms
  • 0
    Пользуясь случаем, хочу спросить. Планирует ли Mail.ru переработать веб-агент? Дело в том, что он включен по умолчанию и при открытии почты в бразерах IE, начинает грузить процессор под 100%. При отключенном веб-агенте такой нагрузки нет.

    Техподдержка рекомендует сменить браузер, но, к сожалению, это не приемлемо в корпоративном секторе.
    • 0
      Какая версия IE?
      • 0
        Internet Explorer 8
        • +4
          Менять ничего не надо, значит у нас баг.
          Напишите мне ваш логин на mail.ru на адрес andrewsumin@corp.mail.ru будем разбираться.
  • 0
    Насчет статьи Игоря Сысоева — читал эту статью уже давноб но с тех пор уже много воды утекло и NodeJS стал другой и V8. Интересно что сейчас Игорь об этом думает?
    • –1
      В Node.js это изначально не было проблемой из-за того что контекст сохранялся.
  • 0
    Андрей, а что за шаблонизатор был до этого?
    Хочется понять, сколько вы проиграли в функциональности. Есть сравнение текущих возможностей js шаблонизатора и возможностей предыдущего?
    • 0
      По сравнению со старым, по функциональности, мы сильно выиграли.
      Там нет переопределения (set и get), там нет средств работы с данными.
      • 0
        А если говорить о более простых вещах, такие как:
        * получить все четные ноды или каждую n ноду
        * узнать ко-во нод в узле
        * counter
        * first elem | last elem | prev | next

        Т.е то, что, часто используется при шаблонизации.
        • +2
          У нас на входе либо json, либо мы используем get('key') дальше операция с данными на JS.
          <fest:script>
          var items = json.data.filter(function(){});
          </fest:script>
          <fest:foreach iterate=«items» index=«i»>

          </fest:foreach>
      • 0
        Если честно Использование set и get выглядит очень избыточно, по сравнению с возможным решением:

        <#:tag _name="a" _action="mail" _params="foo,bar">link</tag:#>

        Результат:
        <а httр://mail.ru?foo=1&bar=2">link</а>

        Параметры foo и bar берутся либо из:
        — функции (имя функции прописывается как _fn=«some_function»)
        — наследуются от родительского элемента
        — переменных окружений
        — явно назначаются (если задать значение параметру типа foo=1, то так и будет)

        Если параметра нет и его значение явно не задано, то название параметра не будет выведено.
        • 0
          Вы зачем такой узкий пример привели?
          В get может находится целый кусок функционала.
          • 0
            А для чего вообще хранить логику в шаблоне?
            • +2
              /page.xml
              <html>
                  <head>...</head>
                  <body>
                      <fest:get name="content"/>
                  </body>
              </html>
              <fest:set name="content">404</fest:set>
              
              /index.xml
              <fest:include src="page.xml"/>
              <fest:set name="content">
                 <div class="page">Hello habr</div>
              </fest:set>
              
              /info.xml
              <fest:include src="page.xml"/>
              <fest:set name="content">
                 <div class="page">Have a nice day!</div>
              </fest:set>
              


              Это хранение логики в шаблоне или нет?
              • 0
                В данном случае никакой логики нет.
                Просто я не понял что вы имеете ввиду под хранением функционала (копия куска кода aka Here document)?
  • +3
    Напомнило zpt.sourceforge.net/
    Я бы не хотел писать эти шаблоны.
    А почта работает действительно быстро.
  • +4
    Но в Mail.Ru Group есть целая команда высококвалифицированных людей, знающих JS, способных самостоятельно написать инструмент, а самое главное — они же им и будут пользоваться.
    Как я понял: Либо высококвалифицированные специалисты знающие JavaScript будут кодить на xslt-подобном языке с javascript-вставками, либо вы привираете насчет квалификации :) В любом случае что-то не сходится…

    С виду тот же xslt, только свой велосипед(сейчас не идет речь о том как он летает). И как человек работавший с xslt и пишуший сейчас на JavaScript скажу, что на xml-подобном синтаксисе писать очень не благодарное дело.

    Вы узнавали у ваших разработчиков нравится ли им fest (если они, конечно еще, писали на чем-то кроме xslt)? Были ли альтернативы синтаксиса? Вообще вы спросили их в каком виде им приятнее писать?

    Разработчики — это самый дорогой ресурс и их время и нервы нужно экономить.

    P.S. А почему не стали транслировать в Cи? Была бы ракета!
    • 0
      В Си на сервере, раз у вас для v8 и так используются спец конструкции, то и наверно результат трансляции разный.
    • 0
      Что не сходится? Есть люди обладающие желанием и квалификацией писать инструмент для себя.
      • +1
        Т.е. получается, что высококвалифицированные специалисты знающие JavaScript будут кодить на xslt-подобном языке?
        • 0
          Это же шаблонизатор, не более, он мог быть написан на любом языке, просто в этом случае серверный язык JS.
        • 0
          Не кодить а шаблонизировать.
          Кодить будут на JS.
        • 0
          И еще раз повторю, мы сами долго думали XML или велосипед.
          Пока выбрали XML как проверенное решение.

          Что будет завтра как знать?
          • 0
            Да, попробуйте узнать у своих разработчиков, что бы они хотели видеть в качестве шаблонизатора. Как они видят шаблонизатор, как бы ИМ удобныее было писать шаблоны (выкиньте на время все эти «плюшки» xml). Любой шаблонизатор можно переделать так или иначе в ракету, сделать строгим, расширяемым, главное, чтобы разработчикам было приятно с ним работать. Надеюсь, вы меня поняли :)

            Я не верю, что класный JavaScript с радостью бы стал писать шаблоны на xml если у него были бы альтернативы.
            • +1
              Мы вместе его и разрабатывали.

              Вообще как я предполагал дисскуссия свалилась в XML не XML, это хорошо, значит по основному вопросу разногласий нет. Если вы не заметили в заголовке статьи ни слова про fest и XML.
              • 0
                Летает быстро — это хорошо, но немаловажна скорость и комфорт разработки, которые вытекает из синтаксиса шаблонизатора.
              • +4
                Использовать nodejs для шаблонизации и отсутствия дублирования кода — мысль здравая
                Использовать xml как шаблонизатор — мысль НЕ здравая
                • –1
                  Меня поражает ваша самоуверенность.

                  Я не работаю в Mail.ru, но мне тоже нравится этот вариант синтаксиса шаблонизатора. Скажу больше — такие шаблонизаторы существуют уже давно.

                  Все «минусы» xml вытекают сугубо из личных предпочтений.
                  Например: долго набирать. Но ведь autocomplete и ZenCoding никто не отменял. Не читается? Но ведь тысячи web-разработчиков используют HTML и XML в повседневной разработке и вполне неплохо себя чувствуют.
                  Все остальные минусы, как-то: поломка синтаксиса при использовании тегов в значениях атрибутов или хранение логики в шаблонах — исключительно минусы реализации, но не как самого XML как такового.

                  Не нужно преподносить своё мнение как единственно верное.
                  И уж тем более ничего «больного» в xml я не вижу.
                  • +3
                    Меня исключительно бесит пихание такого уродского стандарта как xml всюду и вся и я искренне надеюсь, что когда-то эта мания прекратится.
                    • 0
                      Я уже понял как вы любите xml. Вам нужно в ряды W3C, глядишь и жизнь станет лучше.
                      • 0
                        Я w3c не люблю ещё больше, чем xml)
  • +2
    Хорошая статья. Потмоу, что я сам не раз ломал голову, как решить проблему возникающую в любом продвинутом аякс-приложении:

    > два набора совершенно разных шаблонов на сервере и на клиенте.

    От себя добавлю: не только шаблоны, если нужна офлайн работа, придется еще и модельки, их валидацию и хранилище дублировать. Мне пришли в голову такие идеи:

    1) сделать Xml-based язык шаблонов и компилировать его в PHP (или что-нибуль Си-подобное если требуется скорость) и JS для клиента — заведомо FAIL, так как после богатых возможностей PHP с циклами, вызовами хелперов, константами, массивами на нем невозможно будет программировать
    2) сделать шаблоны на PHP, а для клиента сделать транслятор PHP subset в JS — это во-первых, сложно, во-вторых, мало перенести конструкии языка, ведь в шаблонизаторе используются модели (класссы на PHP) и хелперы (PHP-функции) — их-то как перенести, а? Дублировать всю логику на клиентсайде что ли?
    3) (не выход) перенести всю логику на клиента — не подходит, так как без яваскрипта не будет работать.
    4) перейти на сторону тьмы и использовать уродливый тяжелый GWT — опять же, без JS он вроде нормально не работает.

    Таким образом, проблем много, а решения я не вижу никакого. Выхода, как говорится, нет. Стоит отказаться от JSON и гонять с сервера шаблонизированные данные видимо.

    Потому ваш подход все же не впечатляет. Node.JS вместо компиляции в Си++ на сервере, плюс слабые возможности, плюс нельзя в шаблоне использовать хелперы и серверные классы. Слишком много недостатков.
    • 0
      Компиляция в два языка рассматривалась, но она мне очень не нравится.
      Если мы говорим про JS то с обновлением движков как v8 так и остальных, мы будем бесплатно получать новые фичи в шаблонизаторе.

      При компиляции в два языка это не возможно.

      Плюс вы и правда думаете в Mail.ru на сервере только один язык? Сколько компиляторов тогда надо сделать?
  • +3
    Непонятно, зачем придумывать что-то на устаревшем, де факто, XML, когда есть JSON. too enterprisy.

    И вообще, пока лучшего шаблонизатора для JS, чем Jade, я пока не видел.
    • 0
      Я привел целый ряд причин за XML, думаю есть смысл на них ответить.

      Второе, имея JS на сервере сменить один JS шаблонизатор на другой задача на порядок проще задачи притащить JS на сервер.
      • +1
        Андрей, нет причин для использования XML, кроме унаследованных приложений и поддержки корпоративных протоколов (SOAP-based).

        XML более многословен и менее быстр для формирования и парсинга.

        Пора принять JSON как аксиому.
        • +2
          Я в этом уже который год пытаюсь убедить разработчиков Icecast2(там xml и конфиг и вывод статистики)
        • 0
          Скорость парсинга не важна, на продакшене XML нет.
          JSON хорош, мы его используем для данных.

          А Jade это не JSON, не надо лукавить.
          • +1
            Jade — это язык. Речь о том, что XML уступает JSON по фану всегда и везде. Нет причин его использовать.
            • 0
              Вот мы данные в виде JSON принимаем, в чем противоречие?
              • 0
                Как я понимаю, у вас XML — язык разметки? И это мне как раз и не нравится, ибо он неудобен для человека. Тут удобнее Jade, Builder или иные удобные шаблонизаторы.

                Равно как не нравится, как источник данных, тут удобнее JSON.
        • +1
          JSON — это формат обмена данными, к организации когда он никакого отношения не имеет. Но вы наверное имеете ввиду само представление данных.
          У меня как раз есть модуль для генерации CSS из словаря, думаю добавить еще HTML.
      • 0
        Плюсы XML с вашей статьи:
        1. Подсветка синтаксиса и оформление XML на IDE
        2. Валидация XML на IDE
        — все языки/разметки так умеют

        3. XML Napespaces для расширения и использования разных языков
        — это никак не ставит xml выше других — другие языки тоже отлично расширяются

        4. Инструменты для обработки xml: XSD и DTD…
        — все это относится к валидации, другие языки тоже отлично валидируются

        5. XML легко читать
        — да, парсерам легко читать.

        6. Экранировение и фильтрация из коробки
        — т.е. заведомо испорченные данные с XSS-уязвимостью XML спасет (xml который уже транслирован в JS)? Или я не так понял?

        Вы и вправду считаете, что на XML удобно и весело писать код(даже с такой кучей «плюшек» со стороны)?
        • 0
          — все языки/разметки так умеют
          Какие все? Конкретный пример JS шаблонизатора, забудем про скорость, который умеют все IDE

          — это никак не ставит xml выше других — другие языки тоже отлично расширяются
          ок

          — все это относится к валидации, другие языки тоже отлично валидируются
          На первое место я как раз не валидацию ставлю а SAX и XSLT

          — да, парсерам легко читать.
          тут пример пожалуйста, я хочу вывести
          <div class="page" data-base="base">
              <a href="#">link</a>
              <script>console.log()</script>
          </div>
          

          В fest чтобы это вывести нужно ровно это и написать.

          — т.е. заведомо испорченные данные с XSS-уязвимостью XML спасет (xml который уже транслирован в JS)? Или я не так понял?
          Не так XSS это экранирование другого уровня.
          Я имею ввиду отсутвие обратных слешей для экранирования кавычек, например.
          • 0
            Про валидацию пропустим, я имел в виду, что подсветка синтаксиса любого языка имеется в любом IDE — то, что IDE расставляет переносы в XML и подсвечивает атрибуты ничего не дает — у вас как и у любого шаблонизатора нет автокомплита и прочих радостей из коробки.

            > SAX и XSLT
            Вы используете XSLT для генерации оптимизированного кода v8?

            > пример
            Любой шаблонизатор (не HAML-подобный) выведет такой же код. Речь же идет о куче дополнительной писанины, которую несет XML:
            <ul>
            <fest:foreach iterate="json.items" index="i">
                <li><fest:value>json.items[i]</fest:value></li>
            </fest:foreach>
            </ul>
            
            • 0
              Мы используем SAX
          • 0
            Slim:
            div.page data-base="base"
            a href="#" link
            :javascript
            console.log();
    • +3
      XML дает валидацию через всякие Relax NG (альтернатива: писать и вымучивать код валидации шаблона руками, тратя гораздо больше времени). XML дает возможность легко делать всякие блоки с параметрами и много других вещей.

      А Jade (хотя такой синтаксис по моему впервые использовался в HAML) — интересная вещь, но я бы не рискнул его применять, пока сам не потестировал и посмотрел на плюсы/минусы. Как минимум, при его использовании надо полученную от верстальщика HTML-верстку почти целиком переделывать в блоки — это надо либо автоматизировать, либо это лишняя работа. В то время как XML и HTML — братья-близнецы.
  • –5
    А mail.ru наконец-то стала делать нормальные проекты?
    • +3
      Почему «наконец-то»? А Tarantool?
      • 0
        Ну вот, любители МоегоМира стали минусовать
      • +5
        Программисты там лучшие, они ведь не виноваты что у их начальства кривое видение проектов…
        • +3
          Начальство тоже когда были программистами.
  • 0
    > Версия 3.6 ведет себя по памяти прекрасно.

    Андрей, а чего версия? Если node, то 6.3, может быть?
    • +2
      Версия v8, NodeJS у нас на продакшене нет, по краней мере пока.
  • 0
    Не кто ж так картинки вставляет? ;)
  • +1
    Андрей, вставлять в текст статьи картинку размером в 2 мегабайта — storage.futubra.com/preview/262162/1345.png — не очень правильно.
    • +2
      Увлекся текстом, поменял.
  • 0
    Из статьи не понял чем XSLT не подошел…
    • +1
      Скорость, я не говорю что XSLT в принципе медленный но тут он проигрывает. И в XSLT нет нормальной возможности поработать с входными данными, например преобразовать timestamp в красивую дату.

      Ну и наконец почему многие пропускают фразу про единые шаблоны на клиенте и на сервере? ))
  • 0
    Это что, пост вот про эти 500 строк JS-кода? github.com/mailru/fest/blob/master/lib/compile.js 0_o остальное там — парсер XML и деобфускатор JS, я правильно понял?

    Зачем вам XML? Не проще было сразу шаблоны на JS писать декларативно? Парсить XML в JS довольно расточительно. Если вам быстрее парсить XML чем разбирать какой-нибудь нативный для JS формат, то вы определенно что-то делаете не так.

    Вообще, это KISS во всей красе. До этого ведь у вас был XScript-подобный велосипед с пасьянсом и актрисами? Теперь осталось выкинуть ещё либо XML, либо JS. И будет хорошо.

    Либо выкинуть всё, взять что-то вроде sourceforge.net/projects/libctemplate за основу, да зафигачить шаблонизатор встроенный в nginx.
    • +3
      «Зачем вам XML? Не проще было сразу шаблоны на JS писать декларативно? Парсить XML в JS довольно расточительно. Если вам быстрее парсить XML чем разбирать какой-нибудь нативный для JS формат, то вы определенно что-то делаете не так.»

      Расточительно, если вы хоть на секунду подумали что xml существует хоть где-то кроме девелопмента то плохо. Если вы так делаете у себя еще хуже.
      Нативный JS формат. Люди очень любят кидаться такими фразами не приводя ничего в пример.

      «Вообще, это KISS во всей красе. До этого ведь у вас был XScript-подобный велосипед с пасьянсом и актрисами? Теперь осталось выкинуть ещё либо XML, либо JS. И будет хорошо.»

      Тут я не понял смысл, откуда в Mail.ru XScript? И в старом шоблонизаторе точно не было ни XML ни JS.

      «Либо выкинуть всё, взять что-то вроде sourceforge.net/projects/libctemplate за основу, да зафигачить шаблонизатор встроенный в nginx.»

      А фразу про единые шаблоны на клиенте и на сервере вы вообще пропустили.
      • +1
        Да, я и правда чего-то много пропустил. Ок.
    • 0
      Либо выкинуть всё, взять что-то вроде sourceforge.net/projects/libctemplate за основу, да зафигачить шаблонизатор встроенный в nginx.
      habrahabr.ru/post/119582/
      • +1
        Мм, круто. Не знал. Спасибо.
  • –8
    Что-то мне все страшнее и страшнее становится видя надпись Mail.ru. Эпикфейл с CAS ничему не учит (похож максимум на дипломную работу, некоторый код меня просто вверг в такой ступор и истерический хохот, что я долго не мог успокоиться), так и здесь, все даже читать не стал, уже хватило типичной ошибки верстки, когда в контентный шаблон инклудится хедер и футер, что приводит к очень большим проблемам при поиске незакрытого тега, когда шаблонов набирается много, этот процесс затягивается на часы. Я так же раньше делал, когда только пришел в веб, когда же я сделал шаблоны законченные (если тег открыт в шаблоне, значит в нем же и должен быть закрыт) разработка стала проще в разы, а тут видя такое у компании с многолетним опытом.
  • –2
    Я правильно понимаю, что это такой PHP для v8?
    • 0
      Можно подробнее?
      Я совсем не понял мысль.

      JavaScript это язаык PHP тоже язык, а что значит фраза «Я правильно понимаю, что это такой PHP для v8?»
  • +1
    А что-нибудь про XJST или базируемом на нем BEMHTML?
    • +2
      что именно вас интересует?
    • +1
      Конечно пробовали, очень нравится, но пока не укладывается нужное нам время.
  • 0
    Андрей, я подозреваю что вы должны были проводить исследования на предмет того, чем пользуются коллеги по user hammering'у. Поделитесь с сообществом своим анализом, пожалуйста :)
  • 0
    Отличная работа! Весёлые иллюстрации!
    Одна мелочь: почему проигравший Райан вместо .004 получает .00Ч?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка