Pull to refresh

Comments 17

Express староватый выбрали, ну и забыли упоминуть jade который у вас в качестве template engine.
А вот за nodemon спасибо, я как-то и не знал.
jade вроде идёт в экспрессе по умолчанию. Есть нюансы?
Нет, вы немного ошибаетесь. Jade хоть и написан тем же автором по нику visionmedia, но является отдельным проектом. Собственно строчки:

app.configure ->
    app.set "views", "#{__dirname}/views"
    app.set 'view engine', 'jade'

и устанавливают jade как template engine по-умолчанию в express, без них работать не будет. Если мне не изменяет память, а она у меня такая, то если ничего не указывать, то мы получим ejs
Оу, и точно! package.json взял из стпрого проекта, и на версию внимания не обратил.
и jade добавил в описание, да.
А jade лучше чем haml, или это кому как нравится?
Они близки очень (собственно, автор и не скрывает источника вдохновения), но Jade мне показался чище, что-ли (не рябит в глазах от знаков процента хотя-бы).
Jade приятнее, однозначно. Вообще, все круто, чисто, красиво и минималистично.

Но он, собака, самый медленный из всех возможных альтернатив.
Dust.js и Handlebars.js — значительно быстрее. Про DoT.js вообще молчу, хотя у него не самый приятный синтаксис (впрочем, хотя бы нет знаков %, что уже хорошо).

Где-то было даже сравнение производительности. Если что — да, я понимаю, что для кого-то «преждевременная оптимизация...» и все такое, но все-таки это немаловажно. Баланс между красотой и производительностью.
А, вот отчет сравнения на небольших шаблонах на JSPerf.
Мне jade нравится тем, что:

  • есть нормально реализованное наследование шаблонов (чего, кстати, нет в haml'е)
  • стиль кода близок к coffeescript


Вопрос же производительности шаблонизатора так остро не стоял.

В конце концов, если будет надо — думаю, не будет большим вопросом сконвертировать шаблоны из одного формата в другой, а в процессе разработки и сопровождения jade точно удобнее doT.js и прочего подобного (банально читать легче).
Тут я не спорю, в плане синтаксиса Jade прекрасен! Особенно, если учесть, что я тоже предпочитаю CoffeeScript где это возможно. Для небольшого проекта это не так критично, а вот для высоких нагрузок — вполне.

Недавно использовали Node.js не только как REST/JSON/socket-сервисы, но и для формирования некоторых кусочков веб-страниц. И начинает душить жаба, когда понимаешь, что на рендеринг отдается столько времени с Jade (а особенно с «with»), когда «можно и по-другому» :)
Производительность doT притянута за уши слухами. doT по умолчанию не экранирует данные, в отличие от всех своих «конкурентов». Вот и весь секрет. Экранирование данных это самая сложная операция в JS-шаблонизаторах, которая легко может увеличить время в бенчмарках раз в 5-10.

Я попытался сделать бенчмарк, который показывает более объективные результаты. В нём отдельно сравнивается скорость работы с экранированием данных и без, для каждого шаблонизатора, и уже сумма результатов берётся для сравнения, т.к. на деле экранирование дынных при генерации html применяется не редко. Также в параметры передаётся кусок текста нормального размера, который можно ожидать в боевом окружении. Он также серьёзно влияет на результаты. Сравнивать на нескольких коротких строчках — некорректно. И doT в данном случае уже далеко не лидер.

Без экранирования, он немного выигрывает у моего шаблонизатора ECT, т.к. doT имеет довольно ограниченный функционал. ECT содержит в себе систему загрузки шаблона с диска, кеширования и реализацию наследования и инъекций в шаблонах. Это требует некоторых операций, поэтому тут он немного проседает в попугаях. Но дело в том, что эти вещи понадобятся в любом случае, с любым шаблонизатором, если проект не hello world. И нам придётся дописывать их в приложении.

Ну для задач, где экранирование не требуется — не особо то и притянута. Он правда мега-быстр.

Но, согласен, честно было бы сразу сообщить о том, что doT.js далеко не так функционален как Jade и про экранирование. Но скорость Jade понятна — у него необычный и сложный для парсинга синтаксис. Но когда нужна была скорость и функциональность — Handlebars и Dust. Последний отлично работает с экранированием, поэтому и является одним из самых предпочтительных на текущий момент. doT — только для «особых случаев».

Спасибо за ссылку на ECT — обязательно посмотрим.
Конечная скорость шаблонизатора не должна зависеть от синтаксиса шаблонов, т.к. все шаблонизаторы превращают шаблон в JS-функцию и далее выполняется эта функция. В моём бенчмарке скорость парсинга вообще не учитывается. Она совершенно не важна, т.к. компиляция происходит только 1 раз при первом считывании шаблона, далее выполняется уже скомпилированная функция. Скомпилированная функция в общем случае — простая конкатенация строк. Некоторые шаблонизаторы выполняют дополнительные действия внутри этой функции и просаживаются по производительности.

Вот некоторые примеры:

1. Использование with внутри функции. Так по умолчанию делают Jade и EJS. Этот оператор очень медленный, он просаживает данные шаблонизаторы по производительности в несколько раз. Используется для того чтобы можно было обращаться к переменным шаблона без префикса. Т.е. вместо locals.someVar или this.someVar писать просто someVar.

2. Отслеживание текущей позиции в файле для сохранения возможностей дебага. В этом случае перед каждой операцией внутри шаблона, в некоторую переменную сохраняется строка, на которой мы находимся в данный момент. Если во время выполнения шаблона произошла ошибка, мы сможем узнать строку на которой находится вызов плохой функции и исправить её. В большом шаблоне doT это будет сделать довольно непросто.

3. Обработка неопределённых переменных. Шаблон может содержать переменные, которые не будут определены в переданных ему данных. Если никак их не обрабатывать, то после преобразования в строку мы получим «undefined» на месте, где должна быть выведена переменная. А в случае с шаблонизаторами, которые используют with это прекратит работу с ошибкой обращения к неопределённой переменной. В контексте шаблонизатора, я считаю правильным замену неопредлённой переменной на пустую строку. Тогда в результирующем html на этом месте будет пустота. Можно, конечно, в коде шаблона писать что-то вроде locals.someVar || '', но во первых это сильно раздует код, во вторых обязательно где-нибудь всплывёт ошибка, что не вывелся 0, переданный в качестве параметра, т.к. он привёлся к False в условии. ECT сам умеет правильно работать с неопределёнными переменными, поэтому немного проигрывает по производительности doT на не экранированных данных.
Спасибо за подробности.

> «Конечная скорость шаблонизатора не должна зависеть от синтаксиса шаблонов, т.к. все шаблонизаторы превращают шаблон в JS-функцию и далее выполняется эта функция»

Полностью согласен с этим. То, что я имел в виду — синтаксис все-таки может определять то, какая именно функция генерируется и какие проверки содержит. И в Jade все менее очевидно, и необходимо больше проверок внутри уже сгенерированной функции.

Впрочем, может я ошибаюсь, и их возможно сделать полностью независимыми не только в теории. Однако тогда не совсем понятна чрезмерная неторопливость Jade — даже без «with».

Почему не может быть специального режима, в который можно компилировать шаблоны, после того как все отлажено и работает. Убрать лишние обработчики ошибок, прочие тяжеловесные проверки.

Вопрос как продвинутому в теме спецу — чем вызвана такая разница в runtime-производительности разных шаблонизаторов? Не по вышеперечисленным трем пунктам, а в целом? Почему так отличаются сгенерированные функции при схожей функциональности шаблонизатора? И, кстати, насколько влияет на это отдельно третий пункт (про неопределенные данные)?
По большому счёту проверки на наличие данных и сохранение информации о текущей строке не оказывают серьёзного влияния на производительность. Я их привёл выше как пример того, что может ещё делать функция шаблона.

Извиняюсь, что не очень понятно выразился в последнем предложении. Я немного сбился с мысли. Я имел ввиду что ECT проседает относительно doT из-за всех дополнительных возможностей, которые включены в него, а не только из-за проверки данных. Кеширование, наследование, инъекции понадобятся в любом случае в более-менее большом проекте и данный разрыв нивелируется за счёт написания собственных реализаций недостающего функционала на уровне приложения.

Относительно различия производительности схожих по функциональности шаблонизаторов — тут идут в ход микрооптимизации. Тут надо понимать, что соревнуемся мы с операцией конкатенации строки, которая очень быстрая. В такой ситуации любая лишняя операция может погубить всё.

Грубый пример:

var i = '1' + '2' + '3';

намного быстрее чем

var i = '1';
i += '2';
i += '3';

Во время разработки ECT я тестировал множество возможных вариантов написания одного и того же кода с помощью микробенчмарков и выбирал самые быстрые. К сожалению это пока единственный вариант написания экстремально быстрого кода на JS (если не считать знание наизусть всей кухни ECMAScript и V8).

Выглядит это примерно так:

function bench (fn) {
        var t1 = Date.now();
        for (var i = 0; i < 10000000; i++) {
                fn();
        }
        var t2 = Date.now();
        console.log(t2 - t1);
}

var fn1 = function() {
        var i = '1' + '2' + '3';
}

var fn2 = function() {
        var i = '1';
        i += '2';
        i += '3';
}

bench(fn1);
bench(fn2);
Sign up to leave a comment.

Articles