Pull to refresh

Comments 96

Простите, не совсем понятно зачем на клиент coffee-script.js тянуть?
coffee-script.js нужен для компиляции CoffeeScript кода в JS. Я понимаю, что его размер великоват, но это вполне допустимо для сложных приложений где требуется шаблонизация такого уровня на стороне клиента, т.к. там загрузка происходит, как правило, 1 раз за долгое время использования приложения. А вообще, главное предназначение ECT — использование совместно с Node.JS. Поддержка браузеров — приятный бонус.
можно все написать, скомпилировать на сервере и отдавать компилированное на клиент, и уже с компилированным работать. нежели все это проделывать на клиенте, неправда ли?
Конечно можно, и, быть может, этот функционал появится со временем. Но повторюсь — приоритетная платформа для этого шаблонизатора это node.js.
Это не отменяет того факта, что можно скомпилировать и закешировать, зачем делать это каждый раз?
Для работы с node.js это не нужно.
Это смотря сколько шаблонов :)
Функционал предварительной компиляции шаблонов появился в версии 0.5.1. Теперь можно при помощи командной строки или вспомогательных утилит (я использую плагин File Watchers для IntelliJ IDEA) можно компилировать шаблоны и не тащить coffee-script.js на клиент. Такой же функционал предсотавляет compiller middelware для connect/express. Если на сервере используется node.js это может быть более удобным решением.
Можете подробнее объяснить как работает наследование, из примера я не очень понял?
Наследование работает снизу вверх. Т.е. запускается рендеринг шаблона и если в нём директивой

<% extend 'templateName' %>

определён родитель, то после рендеринга первого шаблона запускается рендеринг родителя и в него передаётся результат рендеринга первого. Этот результат в родителе вставляется в необходимое место директивйо

<% content %>

Наследование может быть многоуровневым. По похожему принципу работает переопределение блоков, с разницей в том, что главным является значение самого нижнего шаблона в иерархии вызовов, и его можно использовать в любом из шаблонов «выше».
Это не наследование, а включение.
Это наследование. Т.к. мы включаемся в родителя, можем переопределить для него переменные и блоки. Включение делается через partial.
Это велосипед вместо динамического подключения шаблонов?
Вообще не понимаю как это связано.
Идея супер, однако тянуть coffee-script.js в проект — это реально останавливает от использования.
После gzip средствами nginx coffee-script.js весит 42кб, это на 10кб больше jQuery, который используется на многих сайтах, да ещё с кучей плагинов. Не вижу в таком размере ничего криминального, учитывая специфику применения JavaScript шаблонизатора для сложных JS приложений. Как правило, в таких приложениях скрипты отвечающие за бизнесс логику весят в несколько раз больше. Яркий пример — gmail. Плюс ко всему coffee-script.js осядет в кеше и его не нужно будет загружать повторно.
Почему каждый автор библиотеки считает что только его единственная и неповторимая библиотека будет присутствовать на сайте?

Мы платим необходимостью грамотной настройки и установки nginx, +42кб к коду за шаблонизатор. Нужен ли он нам такой ценой в свете наличия других, гораздо менее требовательных решений?
Шаблоны нужно компилировать в JS-функции на сервере при деплое (Rails Assets Pipeline умеет это легко) — компилировать же их каждый раз на клиенте — потеря ресурсов.

Да и после Haml/Slim писать чистый HTML очень не удобно (есть уже куча JS-портов этих шаблонизаторов).
Шаблоны компилируются в функции 1 раз при первом обращении, а затем кешируются. Для приложений где используется шаблонизация на стороне клиента это вполне приемлимо. ECT разрабатывался преимущественно для использования с node.js. Генерировать функции на сервере для дальнейшего использования их на клиенте не проблема и, возможно, этот функционал появится в ECT через какое-то время.

По поводу Haml/Slim/Jade и других. Тут могу сказать только, что это дело вкуса. Я не отрицаю преимуществ такого подхода, но зависит от привычки. Мне, например, синтаксис шаблонов без тегов ломает глаза. Не отрицаю, что кому-то может быть удобно. Нужно будет добавить Haml в сравнение проихводительности, Jade себя показал очень плохо. Отстаёт больше чем в 10 раз.
Я хочу заметить, что время компиляции абсолютно не важно, поскольку более выгодно компиировать шаблоны один раз при деплое, чтобы на сервере уже были лишь JS’ки.
В тесте производительности замеряется не время компиляции, а время рендеринга. Т.е. уже скомпилированная функция тестируется.
Так у всех шаблонов одинаковый рендер — там просто JS-функция с конкатинацией.
Посмотрите тесты, ссылка есть в топике. Результаты очень сильно различаются.
Потому что у некоторых шаблонизаторов нет кеширования компиляции.
Смотрите внимательнее. во всех тестах функции скомпилированы. Можете вывести значение переменной compiled внутри функции step, чтобы в этом убедиться.
Я вот к примеру использую рэндеринг шаблона прямо в DocumentFragment, время немного увеличивается, зато с лихвой компенсируется во время вставки в DOM, поэтому получается «быстрее чем у других».
Как мне кажется, DocumentFragment нельзя использовать с такими шаблонизаторами, так JS-вставка может находиться посреди тега.
А вы знаете, я тоже так думал, но оказалось всё намного проще. За длительное время я ни разу не сталкивался с подобной проблемой. За основу синтаксиса и «энергии дао» взял ZenCoding стиль. Таким образом, привычные javascript вставки в шаблоне нарушают целостность «энергии шаблона». Простыми словами: Макет должен оставаться макетом. Но что бы не терять мощь шаблонизатора, javascript-ную логику вынес в кастомные тэги. И собственно, шаблон из которого нельзя построить json дерево, является не правильным шаблоном, имхо. Если вы приведёте противоположный пример, буду благодарен. (Кстати я было уже вклинился в ваш диалог ниже) И добавлю, что даже если такой подход и имеет свои недостатки, так они компенсируются лучшей производительностью.
Видите ли, этот шаблонизатор (как и большинство других) не привязаны к HTML и могут шаблонизировать любые строки. Поэтому DocumentFragment в них и нельзя использовать.
Согласен, поэтому я тоже оставил возможность генерировать string из построенного дерева. Хотя основной задачей все же является генерация UI, a в html, это зачастую DOM. Поэтому на это и ориентируюсь.
Посмотрел тест — там просто вызывается шаблон. Никакого разделения компиляции и рендеринга там нет.
Если смотрели тест ECT — то в функции prepare вызов первого рендеринга — загоняет скомпилированную функцию в кеш и дальше используется именно она. Я не вижу смысла выносить компиляцию в отдельную функцию, она используется прозрачно для пользователя. Далее benchmark подсчитывает время за которое функция step каждого теста вернёт 100к отрендеренных шаблонов. В тестах других шаблонизаторов в prepare функция предварительно компилируется, а затем в step Выполняется уже скомпилированный вариант.
Но время засекается до функции prepare ;)
Извиняюсь, моя ошибка. Осталось с прошлой версии бенчмарка. Сейчас исправлю.
Исправил. Результаты практически те же:

Rendering 100000 templates:
ECT:  656ms
Hogan.js:  661ms
Swig:  1337ms
Eco:  1810ms
EJS:  2975ms
Jade:  9533ms
Странно. А какие у вас оптимизации в откомпилированной функции?
Ничего особенного, обычная конкатенация.
Тогда в тесте значит что-то странное.
Весь код открыт. К примеру конкатенирует при помощи .join('') это медленнее чем просто +. Из мелочей складывается результат.
все конечно хорошо, но у меня два НО:
* где уж сравнение к примеру с mustache.js или handlebars.js?
* и ну сколько уже можно шаблонизаторы плодить, который практически ничем не отличаются?
Есть сравнение с Hogan.js, который в свою очередь реализует синтаксис mustache, только быстрее.
Если имеете ввиду отличие от JUST — то оно сильное, т.к. применяется другой встраиваемый язык. JUST будет обновлён ещё 1 раз, для улучшения производительности и на этом его поддержка будет прекращена с рекомендацией использовать ECT. Если имеете ввиду отличие от Eco, то всё описано в статье.
Но опять же, всё это не имеет смысла, так как гораздо быстрее компилировать одни раз при деплое.
Повторюсь, ECT предназначен в первую очередь для node.js. На этом этапе делать полную оптимизацию для использования на клиенте не планировалось, хотя и в текущем виде он отлично работает.
Какая связь между node.js и компиляцией при деплое?
Для использования с node.js прекомпиляция не нужна. Приложение запускается и после первой компиляции шаблон кешируется на всё время работы. Компилируя их Вы сэкономите ~10ms на шаблон во время работы приложения но замучаетесь постоянно деплоить шаблоны. В данном контексте это лишнее. ECT умеет перечитывать изменённые шаблоны без перезапуска приложения и заново их кешировать (если сконфигурировать его с флагом watch).
Понял, шаблон на стороне сервера. Тогда ок.
Мы используем Skim. У нас в проекте лежат файлы вида users_list..jst.skim. Во время деплоя они один раз компилируются в users_list..js и больше Skim-компилятор не разу не вызывается. В итоге не нужно тянуть кучу библиотек на клиента и скорость работы сайты быстрее (так как время на компиляцию шаблонов = 0).
Попробуйте Zen Coding, я долго время был сторонником haml, но после того как опробовал zen coding больше не хочу на haml писать.
Zen Conding помогает только при создании. При чтении и изменении — всё такая же мешанина кода. При удалении кода всё так же легко оставить закрывающийся тег, удалив открывающийся.
В Zen Coding есть куча функций для модификации кода, в том числе правильное удаление тэга (удаляется пара открывающий/закрывающий тэг и меняется отступ кода)
А какие ещё функции?
Неплохие функции, но большинство гораздо проще и красивее решаются препроцессорами. Остальные функции же не так значимы перед более красивым, компактным и организованным кодом в Slim/Sass.
Даже повторение можно красиво записать в Haml/Slim с помощи кода:

- %(home blog forum).each do |section|
  a href='#" section
В итоге повторяемость этого места очевидна при чтении. И править легко.
Тут вы уже путаете шаблонизацию и написание кода. Zen Coding никогда не был и не будет шаблонизатором, его цель — предоставить удобные инструменты для работы с кодом, а не оформление вывода данных.
Нет не путаю, что мешает вам использовать Ruby-код в обычном коде (до его подключения к серверному шаблону)?
Ничего не мешает. Лично я считаю, что для вёрстки статического макета, из которого потом будут сделаны шаблоны, поднимать целое окружение с препроцессорами и тестовыми данными как-то уж слишком избыточно.
Есть много готовых простых приложений типа Compass.app. В любом случае, вы же и так запускаете огромную IDE со сложным расширение Зен Кодинга. Препроцессор и простейщий текстовый редактор с подсветкой синтаксиса — дадут такие же удобства, но буду проще и менее избыточны.
Тем более на куче сайтов фронт сложнее бэкенда, логично что для вёрстки надо использовать сложные компоненты, так как это сейчас очень важная задача.
Хотя лично я против статичных макетов — бэкенд-программисты их очень плохо внедряют. У нас фронт получается гораздо лучше, когда его делают не верстальщики, а фронтенд-разработчики, которые сами шаблонизируют и могут написать CoffeeScript :).
У нас бэкэндеры даже и близко не допускаются к шаблонам, не вижу никакой логики в том, чтобы бэкэндеры занимались клиентской частью.
Согласен. Но очень многие верстальщики передают заказчику просто HTML не задумываясь, как этот файл превратят в шаблон. Стили отдают одним большим файлом не учитывая, что на сервере он может собираться и правильно разложенных мелких файлов и т. п.
Правильно, поэтому это меня огорчило и решил написать очередной велосипед в стиле дзэн — MaskJS.
Однако Zen всё равно не решает проблему низкой информационной плотности кода. Читать всё равно сложнее, так как куча дублирования и лишних символов. Править/удалять несколько удобнее, чем чистый HTML, но всё это неправильное решение проблемы. Проблема в низкой инф. плотности HTML — нужно просто сменить синтаксис и все проблемы решаться сами. Делать сверхумный редактор поверх плохого синтаксиса — не решение.
Проблема в низкой инф. плотности HTML — нужно просто сменить синтаксис и все проблемы решаться сами.

А можно просто пользоваться правильными инструментами и IDE :)
Лично для меня проблема препроцессоров заключается в сложности дебага конечного результата, особенно это касается всяких LESS/SASS
У меня много знакомых перешло на Sass — многие отмечают, что сложность дебага им казалась в начале, но в реальности её не было. Трансляция Sass → CSS очевидна. В инспекторе правда другие номера строк, но это нужно не так часто и можно просто кликнуть на селектор и в комментарии будет оригинальная строка. Тем более в альфа-сборках уже есть SourceMap, которая решает эту проблему.

В любом случае, небольшой сложности — это не так важно, после монстра в виде градиентов (да, Zen Conding позволяет их быстро вводить — но от них просто текут глаза :) ).

Да и у Sass куча автоматизацией в Compass и других плагинах — генерация кривой Бизье для easing по имени, генерация файла шума, автосборка спрайтов.
Отличная работа и интересный подход. Всё сделано на очень хорошем уровне и скорость работы впечатляет
Процентики напрягают, MS недавно избавились от них в своем cshtml. Хотелось бы чего-нибудь подобного.
Обрамляющие теги настраиваются по вкусу опциями open и close, это есть в документации. Данные теги умеют правильно подсвечивать многие IDE, поэтому по умолчанию включены именно они.
Ещё один язык надо изучать… Мне ближе подход, который использует Sebastian Riedel — минимальные добавки для использования возможностей основного языка (Perl, JavaScript) в качестве языка шаблонизатора. Появляется и скорость, и простота изучения, и компиляция шаблонов, и кэширование — см. Mojolicious и Underscore.js в качестве примеров использования.
CoffeeScript был выбран как раз из-за его популярности. Это не велосипед на коленке. На нём пишут приложения многие javascript разработчики, как на сервере так и на клиенте. А учитывая его лаконичность он очень хорошо вписался в роли встраиваемого языка шаблонизатора.
О, и снова здравствуйте, BVadim!
Вот, пост недавно написал, в немалой степени благодаря вам: habrahabr.ru/post/150594/
Почитайте, пожалуйста, хотя, судя по прошлым нашим спорам, вряд ли поможет…
Ну если Вас устраивает response.end в качестве шаблонизатора, я с этим ничего поделать не смогу.
Собственно, я и не сомневался… А всё-таки, попробуйте прочитать пост ;) И подумать.

p.s. В качестве шаблонизатора меня устраивает, например, Dust.js. А если он не нравится — есть ещё миллион других. Ответьте всего на один вопрос, пожалуйста: зачем вы написали ещё один свой шаблонизатор, который вы забросите поддерживать ещё через пару месяцев?
Честно, я хотел добавить dust.js к сравнению специально для Вас, но увы, из коробки он не работает. Установка точно по инструкции отсюда: akdubya.github.com/dustjs/

Node.JS я использую последний стабильный 0.8.9

По поводу поддержки:

github.com/akdubya/dustjs
akdubya authored a year ago

Сколько там стабильных версий node.js вышло за год с обратно несовместимыми изменениями?

Я все свои проекты поддерживаю. Для того чтобы использовать ECT не нужно разбирать новый синтаксис. CoffeeScript довольно популярен. ECT очень быстрый и мощный.

Я ответил на вопрос?

Ваш пост я читал.
Во-первых, оно «из коробки» работает.
Во-вторых, dust.js перешёл в LinkedIn: github.com/linkedin/dustjs (неумение пользоваться поисковиками, кажется, начинает объяснять вашу любовь к велосипедостроению).
В третьих, вы написали, что предыдущий шаблонизатор вы скоро перестанете поддерживать, «рекомендуя всем переходить на новый».
В четвёртых, CoffeeScript, может, и довольно популярен, но это именно новый синтаксис, относительно JS.

На вопрос «Зачем вы написали ещё один свой шаблонизатор?» вы так и не ответили.
«Перешёл» и «форкнули» это разные понятия. Я перешёл по первой ссылке в гугле по запросу dustjs.
Тот, что по вашей ссылке я добавил в сравнение производительности:

Rendering 100000 templates:
ECT:  673ms
Hogan.js:  685ms
Swig:  1390ms
Dust:  1761ms
Eco:  2347ms
EJS:  3411ms
Jade:  9013ms


Разница по скорости почти в 3 раза.

CoffeeScript явно популярнее, чем синтаксис Dust или другого кастомного шаблонного движка. Он уступает тут лишь JS, но зато привносит лакончиность, которая необходима шаблонизатору.

В JUST нельзя было вносить изменения, которые бы убили обратную совместимость у тех, кто его уже установил. Поэтому я создал новый проект. Я написал, что будет обновление производительности, но дальнейшая работа над развитием синтаксиса будет прекращена. В случае несовместимости с новой стабильной версией node.js он будет обновлён.

Отвечаю на ваш вопрос: я опубликовал ещё один свой шаблонизатор, потому что он получился лучше других.
Простите, но если использовать для теста вариант, используемый разработчиками, то результат получается такой:
$ node ./benchmark.js 
Rendering 100000 templates:
ECT:      491ms
Hogan.js: 501ms
Swig:     1112ms
Dust:     302ms
Eco:      1349ms
EJS:      2228ms
Jade:     6293ms
Код шаблона в студию.
То ли интернет, то ли ГитХаб тупят, хотел Pull Request сделать.

Я взял ваш github.com/baryshev/template-benchmark, и немного поменял
$ git diff
diff --git a/dust/dust.js b/dust/dust.js
index 8b7a52b..fc51c52 100644
--- a/dust/dust.js
+++ b/dust/dust.js
@@ -6,12 +6,13 @@ var tplData;
 
 module.exports.prepare = function (data, done) {
        tplData = data;
-       compiled = dust.compileFn(str);
+       compiled = dust.compile(str, 'test');
+       dust.loadSource(compiled);
        done();
 };
 
 module.exports.step = function (done) {
-       var html = compiled(tplData, function(err, html) {
+       dust.render('test', tplData, function(err, html) {
                done();
        });
-};
\ No newline at end of file
+};
Классно. Странно что так различаются варианты вызовов. Буду разбираться. Спасибо.
Из js шаблонизаторов — мне больше всего по душе пришелся swig. Синтаксис такой же как в twig и в django, есть наследование, есть возможность создавать свои теги и фильтры. Главный минус swig для меня — не поддерживаются вложенные блоки (issue) и неизвестно когда будут поддерживаться.
Там реализован похожий синтаксис. Но он кастомный (в отличие от CoffeeScript) и по результатам теста Swig оказался медленнее в 2 раза.
Скачал benchmark, оставил только swig и ect. На приведенном примере
ECT:  513ms
Swig:  1059ms


Добавил main.swig и main.ect, содержащие два пустых блока (test и test2). в tpl файлах сделал extend от этих main шаблонов и добавил переопределение блоков (test2 также переопределялся в пустой блок, а test переопредялся в старое содержимое файла tpl).
ECT:  874ms
Swig:  1087ms


Swig лишь чуть чуть замедлился, а ECT более чем в полтора раза. Если сделать еще один промежуточный уровень наследования, то разница получается совсем крохотной между шаблонизаторами.
Вывод — коробочные тесты не полные, нужно проверять на более полном функционале, а не на hello, world
Можно код изменённых шаблонов?
Дабы не разводить переписку в комментах, посмотри пожалуйста habrahabr.ru/post/151591/#comment_5144568 и отпиши в личку — там разберемся с бенчмарком
и еще вопрос, это нормально, что prepare и step в тестах для ect выглядят одинаково?
module.exports.prepare = function (data, done) {
	tplData = data;
	renderer.render('tpl.ect', tplData, function(err, html) {
		done();
	});
};

module.exports.step = function (done) {
	renderer.render('tpl.ect', tplData, function(err, html) {
		done();
	});
};
Да, в ECT нет отдельного механизма для предварительной компиляции функции. Поэтому в prepare я делаю 1 рендеринг, и функция компилируется и оседает в кеше шаблонизатора.
Кроме того, для таких примеров:
main.swig
{% block test %}
{% endblock %}


tpl.swig
{% extends 'main.swig' %}

{% block test %}
inherit
{% endblock %}


и main.ect
<% content 'test' %>


tpl.ect
<% extend 'main.ect' %>

<% block 'test' : %>
inherit
<% end %>


у меня получается
ECT: 386ms
Swig: 16ms
Интересный результат. Над ним тоже поработаю.
Кроме того,
main.ect:
<% block 'test' : %>
main
<% end %>


tpl.ect:
<% extend 'main.ect' %>

<% block 'test' : %>
inherit
<% end %>


добавляю в ect.js вывод html.length:
module.exports.step = function (done) {
            renderer.render('tpl.ect', tplData, function(err, html) {
                console.log(err, html);
                done();
            });
};


выводится undefined и пустая строка, а не undefined и «inherit»

ЧЯДНТ?
Блок работает немного не так. Определение блока и вывод блока — разные команды. Чтобы вывело значение нужно в main.ect сделать так:

<% block 'test' : %>
main
<% end %>
<% content 'test' %>
да, уже увидел это в документации. в личку написал вопрос про задание значения по умолчанию для блока и вызов родителя в блоке
Sign up to leave a comment.

Articles