JUST — JavaScript шаблонизатор

    Во время разработки своего экспериментального WEB-проекта на Node.JS, о котором я рассказал в двух предыдущих статьях, я столкнулся с проблемой выбора шаблонизатора. Несмотря на то, что готовых решений существует довольно много, мне не удалось найти то, которое бы удовлетворяло меня на 100%. Так родился JUST.

    Конкуренты


    Jade
    github.com/visionmedia/jade

    Этот шаблонизатор достаточно популярен среди Node.JS разработчиков. Он обладает хорошим функционалом и скоростью работы, но содержит и спорные моменты:
    1. Отказ от использования тегов в том месте, для которого они, собственно, и были придуманы. С таким подходом я, мягко говоря, не согласен. Конечно, это очень субъективно, но вид разметки страницы без привычных тегов, взрывает мозг. Верстальщик, далёкий от новомодных технологий шаблонизации, не скажет спасибо, если ему придётся вносить изменения в такой код. Также потребуется дополнительная работа при перенесении вёрстки в шаблоны, что замедлит ход разработки.
    2. Перегруженность функционалом. Любой разработчик старается сделать свой продукт максимально универсальным, но иногда нужно уметь вовремя остановиться. На мой взгляд, Jade уже перешёл эту грань.
    EJS
    github.com/visionmedia/ejs

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

    TwigJS
    github.com/fadrizul/twigjs

    Как можно догадаться из названия, это порт довольно популярного PHP-шаблонизатора на JavaScript. Этот шаблонизатор неплохо сбалансирован, но и его нельзя назвать идеальным. Для реализации логики разработчики добавили свой синтаксис, который должен облегчить разработку. Мне эта идея не кажется удачной, т.к. это увеличивает порог вхождения в разработку. Будь то PHP или JavaScript, верстальщику или программисту придётся тратить время на изучение ещё одного синтаксиса, вникать в его логику и следить за его изменениями. Момент очень спорный. Многие разработчики довольны таким синтаксическим сахаром. Я — нет.

    Просто, быстро, удобно


    Эти три качества легли в основу JUST. Шаблонизатор это она из важнейших деталей любого WEB-проекта. Если не считать полностью закешированные страницы — он работает при каждом запросе. Чтобы на любом этапе развития проекта работа с шаблонами приносила только радость, шаблонизатор изначально должен быть таким, чтобы его хотелось лизнуть.

    JUST это

    • HTML для разметки
    • JavaScript для логики
    • Кеширование шаблонов
    • Автоматическая перезагрузка изменённых шаблонов (только для Node.JS версии)
    • Очень простой синтаксис, но серьёзный функционал
    • Наследование, инъекция, определение блоков
    • Возможность работы как в Node.JS, так и в браузере
    • Высокая скорость работы

    Ближе к делу


    Простой пример шаблонов JUST:

    page.html
    <%! layout %>
    <p>Page content</p>
    

    layout.html
    <html>
    	<head>
    		<title><%= title %></title>
    	</head>
    	<body><%*%></body>
    </html>
    

    Код, который сгенерирует из этих шаблонов и переданных данных конечный HTML, выглядит так:

    var JUST = require('just');
    
    var just = new JUST({ root : __dirname + '/view' });
    
    just.render('page', { title: 'Hello, World!' }, function(error, html) {
    	console.log(error);
    	console.log(html);
    });
    

    Более полный пример можно найти в репозитории на GutHub.
    На клиенте всё работает аналогично. Сначала необходимо подключить JUST к странице:

    <script src="https://raw.github.com/baryshev/just/master/just.min.js" type="text/javascript"></script>
    

    Теперь JUST можно использовать точно так же, как в Node.JS:

    var just = new JUST({ root : '/view' });
    
    just.render('page', { title: 'Hello, World!' }, function(error, html) {
    	console.log(error);
    	console.log(html);
    });
    

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

    В качестве root JUST может использовать JavaScript-объект. Иногда такая возможность может пригодиться.

    var just = new JUST({
    	root : {
    		layout: '<html><head><title><%= title %></title></head><body><%*%></body></html>',
    		page: '<%! layout %><p>Page content</p>'
    	}
    });
    

    Как оно работает


    Шаблон разбирается на части простым алгоритмом и превращается в функцию, которая выполняет конкатенацию строк, а в качестве параметров принимает данные для формирования результата. Эта функция кешируется, и далее работа идёт только с ней. Повторный разбор происходит только при перезагрузке приложения или при изменении файла шаблона (если опция watchForChanges установлена в true). Такой подход позволяет получить отличную производительность. На «Что делать?» страницы с разбором шаблона генерируется 20-60мс. Без разбора шаблона (из закешированной функции) за 1-2мс. Учитывая то, что разбор происходит только 1 раз для каждого шаблона, а приложение живёт долго — временем разбора шаблона можно пренебречь.

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

    Что JUST умеет


    Вывод данных

    Вывод данных в шаблон осуществляется при помощи простой конструкции:

    <%= varName %>
    

    Данные вставляются в шаблон как есть, т.е. они не проходят никакую предварительную обработку в шаблонизаторе.

    JavaScript логика

    В шаблонах можно использовать полноценный JavaScript код. Например:

    <% for (var i = 0; i < articles.length; i++) { %>
    <% this.partial('article', { article: articles[i] }); %>
    <% } %>
    

    или

    <% if (user.authenticated) { %>
    <%@ partials/user %>
    <% } else { %>
    <%@ partials/auth %>
    <% } %>
    

    или

    <%= new Date().toString() %>
    

    Наследование шаблонов

    Как показала практика, наследование это очень удобный инструмент, позволяющий строить страницу снизу вверх. Операция наследования в JUST выглядит следующим образом:

    <%! layout %>
    

    или

    <% this.extend('layout', { customVar: 'Hello, World!' }); %>
    

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

    В родительском шаблоне на месте вставки дочернего нужно вставить такую конструкцию:

    <%*%>
    

    или

    <% this.child(); %>
    

    Инъекция шаблонов

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

    <%@ partial %>
    

    или

    <% this.partial('partial', { customVar: 'Hello, World!' }); %>
    

    если нужно передать в подключаемый шаблон дополнительные параметры.

    Переопределение блоков

    Этот механизм чем-то похож на наследование. В родительских шаблонах, кроме места для вставки дочернего, можно объявлять места для вставки блоков. Содержимое этих блоков определяется в дочерних шаблонах. В отличии от наследования, переопределение блоков работает до самого верха, т.е. из любой шаблон может определить содержимое блока любого из своих родителей, а не только непосредственного. Для определения блока используется следующая конструкция:

    <%[ blockName %>
    <p>This is block content</p>
    <%]%>
    

    или

    <% this.blockStart('blockName'); %>
    <p>This is block content</p>
    <% this.blockEnd(); %>
    

    В любом из родительских шаблонов нужно определить место вставки для блока используя следующую конструкцию:

    <%* blockName %>
    

    или

    <% this.child('blockName'); %>
    

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

    Чего в JUST нет и не будет


    Фильтры данных

    Почти во всех шаблонизаторах присутствуют наборы фильтров, которые позволяют производить манипуляции с данными на этапе генерации HTML. Например: экранирование тегов, приведение регистра символов, обрезание строк. Такой подход в корне не верный и этому функционалу тут не место.

    В комментариях на этот счёт обязательно появятся возражения вроде: «Этот подход не соответствует MVC. Экранирование тегов относится только к представлению данных в виде HTML/XML, а мы можем захотеть вывести их в формате где экранирование не нужно, например — PDF. Поэтому этот функционал должен быть именно в шаблонизаторе».

    Предотвращая споры по этому поводу, сразу отвечу на этот вопрос.

    Шаблонизатор не является компонентом «V» модели MVC в чистом виде. Как частный случай он может им быть, но совершенно не обязан. Компонент «представление» может включать в себя как средство непосредственного построения ответа (шаблонизатор), так и средства подготовки данных для конкретного представления. На этом этапе данные можно подготовить и закешировать. Такой подход позволит не выполнять огромное количество лишних операций с данными. Учитывая, что строковые операции довольно медленные, пренебрегать этим нельзя. Если вам всё равно сколько раз выполнится замена строки, один или десять тысяч, то у меня для вас плохие новости.

    Синтаксический сахар в логических выражениях

    Ещё один модный тренд — изобретать свой собственный синтаксис шаблонов. Чаще всего это сводится к выпиливанию скобочек и т.п. уменьшению конструкций языка. Выигрыш от этого очень сомнительный. Разработчик должен знать ещё один синтаксис и надееться на то, что автор шаблонизатора в новой версии не сделает его «ещё удобнее», и его шаблоны продолжат работать после обновления. Нативный синтаксис языка знают все, кто причастен к разработке. Новый разработчик может сразу же влиться в дело, а не вникать в работу супер-удобного синтаксиса. Верстальщики, которым чаще всего нет особого дела до того, как там программисты режут их творения на куски, могут сами внести небольшие изменения в шаблоны и даже подправить логику, т.к. JavaScript они всё же знают по долгу службы.

    Ложка дёгтя


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

    Для обращения к переменным внутри шаблона я вижу три пути:
    1. Обращение к переменной через конструкцию %object%.%var%. Например, this.varName или data.varName. В таком случае при обращении к undefined значению ошибки не будет, и в шаблоне на этом месте появится пустая строка, но в итоге мы получим сильно разросшиеся шаблоны из-за префиксов переменных.
    2. Автозамена прямого обращения к переменной на обращение через конструкцию %object%.%var%. Например, если в шаблоне есть конструкция <%= varName %>, при его разборе мы можем легко заменить её на this.varName, таким образом избавившиь от ошибки при неопределённой переменной. Но обращения к переменной могут быть не только в конструкции вывода данных, но и в логике. Например, обход массива в цикле или участие в условных операторах. Без дополнительного синтаксического анализа, в таких местах нет возможности заменять обращения к переменным автоматически.
    3. Добавление объекта с данными шаблона в область видимости при помощи with. Поместив объект с данными в область видимости, мы можем обращаться к ним без префикса объекта, но платим за это ошибкой при обращении к неопределённой переменной.
    На данный момент я не знаю как обойти эту ситуацию без дополнительного синтаксического анализа шаблона. Если кто-то видит красивое решение для данной проблемы — мне будет очень интересно о нём прочитать. Пока это нужно принять как особенность данного шаблонизатора. Хотя в каком-то смысле такой подход дисциплинирует и не даёт раскидываться неопределёнными переменными.
    Share post

    Similar posts

    Comments 124

      0
      Скажите пожалуйста, а запустить jQuery + Templates в node.js нельзя?
        +2
        Можно, я пробовал.
        Но jQuery требует нескольких зависимостей, некоторые из которых нативные. А нативные зависимости в ноде — это тот ещё геморрой, если речь идёт о проекте с большим количеством разработчиков.
          0
          Я использую Node.JS + JQuery + CSSFramework, и проблем нет, о каких зависимостях вы говорите?
            +1
            загляните в node_modules.
            можно всё разрешение зависимостей переложить на npm, но тогда надо следить чтобы на всех машинах была примерно одинаковая его версия, или ждите внезапных OMGWTF.
              0
              Понятно. Можно использовать JQuery без npm, как обычный модуль подключать.
                +1
                тогда вам придётся включать сборку нативных модулей (от которых жкварь зависит) в свой мейкфайл, и следить за тем, чтобы они могли собраться например под маком или виндой.
          +1
          Я не пробовал, но не думаю, что это хорошая идея. Лучше использовать инструменты по их назначению.
            –1
            А чем jQuery + Templates не подходит по назначению для решения вашей задачи?
              +1
              1. Я не понимаю зачем мне тащить jQuery в node.js, когда эту задачу можно решить без него и сделать это лучше.
              2. Мне не нравится этот шаблонизатор.
              3. JUST работает одинаково и на клиенте и на сервере без каких-либо зависимостей, что в определённых ситуациях даёт профит.
          +1
          > Один неприятный момент в JUST всё же имеется. Если в шаблоне есть обращение к неопределённой переменной, то при его обработке возникнет ReferenceError при обращении к несуществующей переменной.

          Появился вопрос, а какое поведение в этом случае хотелось бы видеть?
            0
            Получить в результате на этом месте пустую строку, т.е. как будто её и нет.
              +1
              Хотелось бы, чтобы шаблонизатор отдавал в этом случае пустую строку и избежать постоянного <% if (some.variable) { %>
                0
                Если использовать объект, как Вы написали, то шаблонизатор вставляет пустую строку. ReferenceError возникает только если нет объекта первого уровня, в данном случае если не будет определён объект some.
              0
              Помоему у вас получился не-до-ECO: github.com/sstephenson/eco. Он достаточно популярен. Я крайне рекомендую еще глянуть на CoffeeHaml (https://github.com/9elements/haml-coffee).

              Jade самый ужасный шаблонизатор для ноды. Он жутко глючный и сам синтаксис спорный. Я не понимаю почему некоторые люди считают его лучше HAML'а – структурно он доставляет куда больше проблем, чем решает. Но это личное дело каждого.

              В общем, да, Jade не очень. И, конечно, от JUST'а хуже не будет. Но я бы на вашем месте все-таки не развивал его.
                +1
                Хотя я только что осознал, что оба этих шаблонизатора работают только с Coffee. Прошу прощения. Я настолько привык к Coffee, что для меня чистый JS стал сродни ассемблеру :).
                  0
                  Вообще-то не некоторые, а многие. Порт Jade на Ruby, — Slim, — завоёвывает всё большую популярность в RoR/Sinatra-сообществах, например.
                    0
                    Это личное дело каждого. Наверное вопрос вкуса. Мы очень долго пытались адаптироваться и в огромных JS-шаблонах, где оно родное. И в рельсах. Не выходит. Нам с HAML'ом удобнее. Это я говорю после полугода адаптации с Jade.
                  0
                  «В шаблонах можно использовать полноценный JavaScript код.»
                  Зачем?!
                    +1
                    Условия, циклы, простые выражения на языке который понятен всем и который не нужно дополнительно разбирать.
                      0
                      Условия, итераторы и выражения не требуют синтаксиса ЯП для реализации. Моему IDE (Intellij IDEA), к примеру, не понятен такой синтаксис. Т.е. я не могу пользоваться ни автокомплитом, ни подсветкой, ни рефакторингом. Даже фолдинг работать не будет.
                        +2
                        Зато создание своего яызка и его разбор требует дополнительного кода от разработчика, его исполнения от процессора и его понимания от того, кто пишет шаблоны. Нативный язык в данном случае я считаю лучшим вариантом. Каждый второй шаблонизатор предлагает свой язык, Ваша IDE все их понимает?
                          0
                          Да не нужно создавать свой язык. У вас тоже, как минимум, нужно запомнить что делает та или иная директива.

                          Моя IDE не все их понимает, конечно же. Собственно, поэтому я и написал свой шаблонизатор, что бы любой IDE мог его понять. Включая автокомплит, рефакторинг, фолдинг, подсветку.
                          Более подробно рассказывать не буду, так как это пока в разработке, да и статья не о нём.
                      +1
                      Чтобы не изобретать своего синтаксиса конечно же… для доступа к значениям полей объектов, вызова методов и т.п.

                      Я, например, приверженец 'старых методов' использования шаблонов, и для экранирования опасных символов использую вызов метода — html(account->name), кто то захочет использовать для этого новый класс, кто то метод у объекта… и для всех изобретать свой велосипед или заставлять плясать под свою дудку?
                        +1
                        «Чтобы не изобретать своего синтаксиса конечно же»
                        Так он уже изобретён. Сравните, к примеру, с Dust. Различия в директивах есть.

                        «для доступа к значениям полей объектов, вызова методов»
                        По-сути это одно и тоже и не требует синтаксиса ЯП.

                        «для всех изобретать свой велосипед или заставлять плясать под свою дудку?»
                        Данные проще и лучше обработать до входа в шаблон. К примеру код из статьи:
                        new Date().toString()

                        Очевидно же, что лучшим решением будет сохранить дату в данные перед передачей последнего в шаблон.
                          0
                          Этим примером я показал что там работает полноценный JS, которые не проходит через какой-либо парсер. Я не в коем случае не призываю делать так в шаблоне. В статье я упомянул о том, что данные нужно подготавливать заранее. Но иногда такие вещи могут понадобиться.
                            –2
                            Не могут. А если понадобятся — вы что-то делаете не так.

                            Я и тут категоричен, потому что испытал всё это на своей шкуре. Налепить костылями можно что и где угодно, но Костыльно-Ориентированное Программирование — это не пример для подражания и, уж точно, не повод писать статьи.
                              0
                              Вы придрались к мелочи. Я уже написал что это не то для чего я его делал. Я не призываю писать код не относящийся к шаблону в шаблоне. И шаблонизатор тоже к этому не призывает. Я говорю о том что там работает JS. Если Вы знаете что так делать не нужно Вы же не будете писать костыли? Или Вам обязательно нужно создать мини-язык на котором написать костыль будет невозможно?
                        +1
                        Мсье – теоретик? :) Затем же, зачем они нужны на сервер-сайде. mustache – это утопия.
                          +1
                          Посмотрите, например, на dust (ссылка есть ниже). Вся логика должна быть в коде, а в шаблонизаторе — только вывод данных.
                            +1
                            Я не предлагал выносить логику программы в шаблон. В шаблоне есть логика шаблона. Такие вещи как циклы для вывода данных, условия при которых могут подключиться разные подшаблоны. Я против использования для этих целей велосипедного языка, когда на обычном JavaScript это сделать очень просто. И это будет понятно, всем кто заглянет в шаблон а не только тем, кто прочитает документацию по %шаблонизаторнейм%. К слову только парсер шаблонов dust (для создания этого самого синтаксиса) содержит почти в 5 раз больше кода чем весь JUST (а не только парсер).
                              –1
                              У вас используются велосипедные ключевые символы. В чём разница-то? Dust, например, не имеет встроенного языка, за исключением необходимой шаблонам логики, при этом удобнее в использовании, чем ваш шаблонизатор, судя по статье. И не позволит лепить такие костыли, вроде логики в шаблоне.
                                +1
                                И в чем смысл мне что-то запрещать? Если я захочу – я не буду использовать сложную логику и в JS-инлайнах. Если я захочу — я и с дастом найду способ смешать представление и логику. Сколько лет уже прошло, а люди никак не поймут – запреты не помогают решить проблему отсутствия знаний.

                                А вот тем, кто понимает что он делает, это проблемы создает огромные.
                                  0
                                  Просто, в данном контексте, с dust намного меньше шансов прострелит себе ногу случайно.
                                    +1
                                    Orly? Вы действительно можете случайно написать гору JS-логики в шаблоне _случайно_? Ну перестаньте вы притягивать факты, norlin :). Я же говорю – в теории Вы правы. Практика, однако, разрушает все эти воздушные замки на корню.
                                      –1
                                      Новичок может (и, скорее всего, будет) писать горы js в шаблоне. Зачем сходу приучать к плохому?

                                      По вашей логике можно и до такого дойти: «зачем нужны права на вождение, если человек хочет — он и с правами аварию устроит, пусть за руль садятся все, кто захочет».
                                        +2
                                        Вы предлагаете с детства детей заставлять ходить на ходулях потому что пока они учатся ходить они могут подвернуть ногу. Как видите, все работает и без ходулей. Такой подход мало того, что не поможет – он ухудшит ситуацию. Они будут писать горы плохого кода чтобы решить проблемы, которые не могут решить во вьюхе. Самое страшное, что логику в шаблоне видно сразу и можно сразу на QA выпилить. А тот код еще пойди найди.

                                        Про права – это вообще фарс. Учите новичков не писать логику в шаблонах когда там есть JS, да. Я к этому и призываю. Это вы хотите давать программировать всем, кто не знает даже азов MVC. Не надо менять нас местами.
                                          –2
                                          Не надо рассказывать мне о том, что я предлагаю и что пытаюсь сказать.

                                          Я предлагаю две вещи: не плодить велосипеды и стремиться к минимализму.

                                          А лепить поддержку полноценного языка там, где для хорошего и правильного подхода он не нужен (вы, вроде бы, с этим согласились) — это лишнее.
                                            +2
                                            Не плодите велосипеды. Не создавайте новый JS. Минимализм. Один язык для всего. Вы в своих собственных комментах сами себе противоречите. На этом я предлагаю дискуссию завершить.
                                              –2
                                              Приведите хотя бы два моих комментария, которые друг другу (по вашему мнению) противоречат.

                                              Либо я где-то ошибся, либо ошибаетесь вы, потому что вот уже 3 или 4 статью этого автора в комментариях я пытаюсь последовательно объяснить свою точку зрения и обосновать, почему эти статьи не то чтобы не нужны, а вредны для тех, кто осваивает javascript и ищет информацию по нему и по node.js в частности.
                                                +1
                                                Я очень долго терпел ваши комментарии. И не хочу переходить на личности. Но по моему мнению Вы не достаточно компетентны в этом вопросе. Ваши аргументы притянуты за уши чуть больше чем полностью, а полноценной альтернативы, которую можно было бы обсудить/покритиковать/похвлить Вы не предлагали. Вы просто со всем не согласны. А это признак самизнаетечего.
                                                  –2
                                                  Про переход на личности я ответил в другой ветке комментов.

                                                  Про то, что мои слова не надо перевирать — тоже сказал.

                                                  А вы просто подумайте, чей это признак, отрицать всё, что вам не нравится и считать что вы один знаете истину.
                                                    0
                                                    Я делюсь своим опытом. Как это принимать дело каждого.
                                                      –2
                                                      Опыт опыту рознь. А мне очень не хочется увидеть через год-два появление быдлокодеров на js, про которых будут говорить примерно то же, что сейчас говорят про большинство php-кодеров.
                                                      Те, кто будет воспринимать ваши статьи как пособие, вырастут, скорее всего, именно в подобных быдлокодеров.
                                                        0
                                                        Если вы считаете мой код быдлокодом — то покажите свой. Я не согласен с Вашим мнением.
                                                          –2
                                                          1. Я не говорил, что ваш код является быдлокодом. Речь о подходе к разработке, а не о непосредственно кодировании.
                                                          2. Когда мой код будет готов к публичной демонстрации и обсуждению — тогда я его и покажу. Сейчас там полным-полно говнокода и костылей, но именно в самом коде.
                                                          3. Главное — каким образом мой код изменит что-либо в вашем коде или подходе?
                                                            0
                                                            1. В предыдущем комментарии Вы сказали что те кто прочитает мои статьи вырастут в быдлокодеров. А теперь говорите что быдлокодом не называли.

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

                                                            3. В моём подходе что-то изменить может только моё дальнейшее развитие. Мне по большому счёту без разницы какой у Вас там код. Я это сказал к тому, что Вы тут так яростно наезжаете на мои решения не показывая свои, тем самым вводя в заблуждение читателей, т.к. они не могут оценить высказывания без своего субъективного сравнения подходов.
                                                              –2
                                                              1,2. Ещё раз, я говорю не про ваш конкретный код, а про ваш подход. Кучи левых модулей, велосипед-шаблонизатор и т.п.

                                                              3. Если я выложу свой код сейчас, 99% придирок и комментариев будут вроде «Ахаха, да у тебя тут цикл не оптимальный!» или «Я так и думал, вон у тебя там метод не тот используется\переменные не так объявлены\функция не так оформлена». А это всё не имеет почти никакого отношения к теме спора.
                                                                0
                                                                В отличие от Вас выбор этих модулей и написание этого шаблонизатора я довольно хорошо обосновал. Вы же только критикуете и придираетесь к мелочам ссылаясь только на то, что считаете это не правильным. Точно так же это может написать вообще любой человек мало знакомый с тематикой или не знакомый с ней вообще. Если вы с решением не согласны, то пока нет:
                                                                • Обоснованных замечаний (а не просто мне не нравится)
                                                                • Удачного примера реализации вашего подхода описанного до такой степени конкретики, чтобы их можно было наглядно сравнить
                                                                • Хотябы ещё кого-нибудь кто бы, ваш подход поддержал


                                                                это пустая болтовня.

                                                                У меня всё работает, работает быстро, код поддерживать просто, ничего не падает, сервер легко справляется с нагрузками и я счастлив. Моё мнение вы не поменяете, у остальных — своя голова на плечах.
                                                                  –1
                                                                  Вот же упёрся!
                                                                  Я так же «довольно хорошо» обосновал, почему большинство из тех модулей не нужны.

                                                                  То, что делать велосипеды — это плохо, это тоже пустая болтовня?

                                                                  Такой подход, как у вас, у меня был в старших классах школы, когда я с другом начинал осваивать Delphi. Искал в Сети подходящие компоненты и склеивал их друг с другом своим кодом. Оно всё тоже работало, не падало и т.п.
                                                                  А сейчас я осознаю, что там где можно написать document.getElementById('id') я так и напишу, а не буду ради этого подключать jQuery. Впрочем, когда человек упирается и отбрасывает сходу все аргументы, когда вокруг большинство это всё поддерживают, потому что сами поступили бы так же, спорить бессмысленно.

                                                                  Чем больше подобных кодеров, тем выше будут цениться программисты ;)
                                                                    0
                                                                    Вы так легко присваиваете ярлык «велосипед» всему что написали не Вы что мне даже становится смешно.

                                                                    Если document.getElementById('id') это всё, для чего Вам нужен jQuery то, ок. Или jQuery тоже велосипед?

                                                                    По поводу ценности программистов оставлю без комментариев, т.к. не хочу спускаться к оскорблениям.
                                                                      –1
                                                                      Да что за бред-то? Велосипедом я называю 100500ый шаблонизатор для javascript, которых на любой вкус и цвет уже понаделали все кому не лень.

                                                                      Вы бы или помолчали или прочитали, на что отвечаете. Если мне надо только getElementById — то я НЕ буду для этого прикручивать jQuery. Где вы видели, что я что-то писал о том, для чего я jQuery всё-таки использую?

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

                                                                          Обо мне вы можете думать что угодно, но очень жаль, что вы не сделали выводов о своём подходе. И жаль новичков, которые будут находить в том числе и эти ваши статьи.
                                  0
                                  Я не понимаю, что Вы сейчас пытаетесь доказать? Тут на вкус и цвет. Я сделал так как посчитал нужным. Оформил документацию и выложил его в open source. Я считаю, что он достаточно проработан для этого и мне не стыдно его показывать людям. Использовать его я никого не заставляю и денег ни у кого за это не беру. Если Вам такой подход не нравится вы можете использовать dust или любой другой шаблонизатор.
                                    –2
                                    Я пытаюсь объяснить, что использовать полноценный язык внутри шаблона — зло.
                                      +1
                                      Не используйте.
                                        –1
                                        И не использую. Я же вам пытаюсь это объяснить.
                                        Впрочем, судя по тому, что у вас уже и лицензия на этот шаблонизатор написана, и предупреждение об «as is», вы всерьёз будете стоять за этот свой велосипед.

                                        Собственно, это тот же самый спор, который начался в комментах к предыдущим вашим статьям. Только там я был против использования чужих велосипедов, а тут — против написания вами ещё одного, не приносящего ничего нового.
                                          0
                                          Да, я заметил что Вы против всего. Только вот я не видел Ваш код, а судя по маленьким наброскам из комментариев к предыдущей статьи, он в довольно плачевном состоянии.
                                            –1
                                            Ну так протрите глаза, судя по всему, вы не замечаете ничего, что вас не устраивает.
                                            Продолжать переход на личности не собираюсь.

                                            Как раз прямо перед тем, как я завязал этот спор в вашей первой статье, я почти набрал кармы для написания статьи о своём проекте.

                                            Наброски были не для того, чтобы код показать, а чтобы попытаться донести принципы, которые я стараюсь объяснить. Не получилось, к сожалению.

                                            Но, в любом случае, писать статьи про велосипеды я не собирался и не собираюсь.
                                +2
                                Да. Это так разумно – придумать свой очередной кривой синтаксис в добавление к тому, что уже есть в JS. А потом постоянно спотыкаться об необходимость написать килобайты обвязки вместо одной элегантной строчки. Я еще раз акцентирую ваше внимание: есть теория, а есть практика. Вы мне здесь можете изложить только теорию. И в ней это еще как-то можно за уши притянуть. На практике это просто не работает.

                                Даст (и handlebars) – яркое тому доказательство. Если бы то, что вы говорите работало, люди бы использовали чистый Mustache и в ус не дули бы. Но нет, не хватает. Почему? Потому что в сложных приложениях вьюшки не описывают структуру данных, как бы вам этого ни хотелось. Они описывают структуру данных, зависимую от обстоятельств. Эффективно описать динамичную структуру данных без динамичных методов – невозможно. Поэтому люди создают эту крипоту, язык в языке. А можно было использовать JS и не плодить сущности.
                                  –1
                                  Значит инструмент не достаточно гибок (Mu, Dust). Но это не значит, что подход в корне не верный. К примеру, в ваших данных часто используется некая структура, которая требует больше внимания к обработке, чем забитые if/each (это имелось ввиду?).
                                  Значит инструмент (шаблонизатор) должен позволить вам вынести логику обработки этой структуры из шаблона в некий модуль/плагин, а не реализовывать её каждый раз в каждом конкретном месте.
                                    0
                                    Точно. Недостаточно гибок. Я вам скажу какой инструмент достаточно гибок. Javascript. И не надо пытаться сделать Javascript внутри Javascript. Это бессмысленно, понимаете? Это имеет смысл только если вы сделаете более удобный Javascript. Но и он уже есть – это CoffeeScript.

                                    Некий модуль плагин, в общих терминах – это хелпер. И это есть в Javascript. Обычное замыкание. На уровне языка. Ничего не надо выдумывать, ага?

                                    Я ровно про это и говорю. Для полноценной удобной работы Dust надо превратить в Smarty. То есть язык в языке. И зачем?
                                      0
                                      Я и не пытаюсь сделать JS в JS и не предлагал этого. Это бессмысленно потому что никому не нужно.

                                      Вспомним PHP. Любому начинающему кодеру известно, что PHP позволяет обрабатывать html и php код вперемешку в одном файле. Опытным же кодерам известно, что это быдлокод.

                                      А теперь посмотрите на код шаблона для этого (и не только) шаблонизатора. Ничего не напоминает? Нет? Что, это похоже на php в html? Т.е. на быдлокод? Правильно.

                                      Даже хуже, потому как в случае с php мой IDE продолжит работать с кодом. А с таким шаблоном (js-кодом), увы, нет.
                                        +2
                                        Вы занимаетесь софистикой. Всем известно, что на PHP долго писали приложения, которые смешивали логику и представление. Не потому что язык такой, а потому что так писали. Это быдлокод. Когда у вас есть отдельные php-файлы для логики и представления – чем же это быдлокод? Тем, что Вам так кажется? Так это Ваше личное мнение, которое даже к теории программирования не относится.

                                        После того как появился Smarty очень многие начали с него слезать ровно на PHP-вьюхи. И все у них хорошо. А в мире Ruby on Rails и вовсе с самого начала такие шаблоны были. И никто не говорил, что все рельсы – это быдлокод ;).

                                        Вы сами себя убедили, а теперь пытаетесь убедить меня. Это называется фанатизм. Давайте либо факты, либо закончим этот разговор.
                                          +1
                                          На одном из проектов используем шаблонизатор собственной разработки основанный на нативном PHP (похожий шаблонизатор был в symfony 1.4). До этого работали и со Smarty и с XSLT. Как по мне — подход с PHP выигрывает, как по скорости работы так и по удобству. Быдлокод это когда используют PHP+HTML не зная, что можно сделать по другому. Когда это делается с умом — это не быдлокод. Я говорю про то, что запросы к базе никто оттуда не делает, к моделям и контроллерам не обращается. PHP там используется только для логики шаблона. Как и JavaScript в JUST. Да, это даёт возможность набыдлокодить, но не обязывает делать это.
                                            0
                                            И еще. Я просто положу это здесь: david.heinemeierhansson.com/arc/000405.html
                                              0
                                              Любому начинающему кодеру известно, что PHP позволяет обрабатывать html и php код вперемешку в одном файле. Опытным же кодерам известно, что это быдлокод.

                                              Чего? Сенсей, научи как лучше.

                                              По-поводу ЯП в шаблонах — вы либо делаете по шаблону на каждую линию поведения приложения, либо используете те или иные механизмы контроля над представлением.
                                              Я не понимаю, почему вы не согласны с inossidabile, он говорит очевидные вещи. Проблема не в том, что разрешено, а в том кто программирует. А набыдлокодить я с чем угодно могу — запрещай не запрещай.
                                    0
                                    В том же Jade я не представляю как можно без него сделать хоть сколько-нибудь сложную страницу. JS-вставок приходится использовать очень много. Но, возможно, я плохо понимаю этот язык.
                                    0
                                    Используйте dust и будет вам счастье: github.com/akdubya/dustjs
                                      0
                                      Я рассматривал его. Там свой синтаксис для логики. Я не нашёл там наследования снизу вверх. Он раз в 6 больше по объёму кода.
                                      На данный момент я уже не выбираю шаблонизатор. Я сделал JUST таким, каким считаю он должен быть и соответственно теперь использовать буду его. В этой статье я поделился им с сообществом, быть может он понравится кому-то ещё.
                                        0
                                        Суровый вы человек, Вадим.

                                        Раз уж все сделано, добавьте свою библиотеку куда-нибудь сюда: jsperf.com/dom-vs-innerhtml-based-templating/146 сравним по производительности.
                                          0
                                          Поддерживаю
                                            0
                                            Как только появится немного свободного времени обязательно добавлю. Будет интересно сравнить.
                                          0
                                          Кстати, да. Как раз хотел написать про dust. Классный шаблонизатор, никакого мусора вроде «в шаблонах можно использовать полноценный JavaScript код». Именно шаблонизатор, а не «yet another programming language».
                                            0
                                            Кроме того, благодаря хорошей компиляции, dust лидирует по надежности и производительности среди других таких же функциональных шаблонизаторов. В том числе и поэтому он используется в самом крупном, насколько я знаю, приложении на node.js — мобильной версии LinkedIn: goo.gl/uIbjg
                                          +1
                                          Интересно, какое место в ряду критики занимал бы яндексовский BEM. Он, вроде бы, рекордсмен по собственным правилам и синтаксисам.
                                            –1
                                            БЭМ — это не шаблонизатор. Шаблонизатор в нем — XJST, который уделает любое поделие в этом посте в щепки, как по скорости, так и по возможностям. Как и fest
                                              0
                                              Необоснованно. Волшебства там нет.
                                                0
                                                XJST пока проигрывает fest по скорости на порядок.

                                                Во возможностям он выигрывает на порядок.
                                              0
                                              Интересно, вы охарактеризовали Jade как шаблонизатор, обладающий хорошим функционалом. Я наоборот сильно мучаюсь от его слабости, да он лаконичен, но при этом примитивен до нельзя. Приходится использовать множество js-вставок.

                                              Ещё вы написали о том, что, по вашему мнению, в шаблонизаторе не место экранированию, работе с lower|upper|camel|*-case и т.д. Не хочу спорить об идеологических соображениях, но зачем создавать себе лишние хлопоты? Я использую Jade как V в MVC, без доп.приготовлений. Привычка после XSLT. И решительно не понимаю, зачем поступать иначе.
                                                0
                                                lower, upper и т.д. это строковые функции. Если страница не закеширована целиком, то шаблонизатор работает, соответсвенно эти функции выполняются. Во многих случаях большая часть страницы хотя бы какое-то время остаётся неизменной. В этом случае если при изменении контента мы заранее подготавливаем его к выводу, то эти функции преобразования строк не будут работать до следующего его изменения. Если мы говорим о замене тегов на html-конструкции это может быть довольно заметно если у вас сотни тысяч генераций страниц. Для небольших проектов — может и нет смысла так заморачиваться.
                                                  0
                                                  Полагаю, что если так остро стоит вопрос производительности, то можно перенести шаблонизатор на клиентскую сторону, Jade, вроде бы, так умеет. Касательно частично-изменённого содержимого… На мой вгляд, проблема правильного кеширования контента — одна из самых сложных проблем, с которыми может столкнуться web-разработчик. Ваш путь усложняет всё до предела, добавляя новую прослойку, стоит ли оно того? Мне кажется проще докупить железа, нежели кормить тех программистов, которые будут потом в этом разбираться.

                                                  Хотя, учитывая наш прошлый с вами спор, мне кажется, что мало кто согласится работать в сложном проекте, который написан по принципу «втопку фреймворки», «даёшь полную свободу каждому модулю» и т.д. Или я вас там не правильно понял? В совакупности, полученная от вас информация, склоняет меня к тому, что ваши проекты для стороннего человека — хаос. Я не прав? Ничего личного.
                                                    0
                                                    В нашей команде проблем с пониманием не было. Я бы не сказал, что код усложняется. Делается всё это как раз наоборот — для упрощения. Всё что пишется — делается не на коленке, а основательно с рачётом на то что любой квалифицированный разработчик сможет понять как это работает не прибегая к документации либо используя её по минимуму.
                                                      0
                                                      Полагаю, что квалифицированных javascript-программистов мало (сплошные $('#some').animate() — кодеры). Учитывая вышеописанные обстоятельства, я сильно удивлён, видимо вам очень повезло. Ну или вы склонны видеть всё в других тонах :)
                                                0
                                                Использую движок JavaScript Templates, лаконичный и мощный.
                                                JS:
                                                    var data = {
                                                        products : [ { name: "mac", desc: "computer",     
                                                                       price: 1000, quantity: 100, alert:null },
                                                                     { name: "ipod", desc: "music player", 
                                                                       price:  200, quantity: 200, alert:"on sale now!" },
                                                                     { name: "cinema display", desc: "screen",       
                                                                       price:  800, quantity: 300, alert:"best deal!" } ],
                                                        customer : { first: "John", last: "Public", level: "gold" }
                                                    };
                                                    var result  = myTemplateStr.process(data);
                                                    someOutputDiv.innerHTML = result;
                                                

                                                Шаблон:
                                                    Hello ${customer.first} ${customer.last}.<br/>
                                                    Your shopping cart has ${products.length} item(s):
                                                    <table>
                                                     <tr><td>Name</td><td>Description</td>
                                                         <td>Price</td><td>Quantity & Alert</td></tr>
                                                     {for p in products}
                                                         <tr><td>${p.name|capitalize}</td><td>${p.desc}</td>
                                                             <td>$${p.price}</td><td>${p.quantity} : ${p.alert|default:""|capitalize}</td>
                                                             </tr>
                                                     {forelse}
                                                         <tr><td colspan="4">No products in your cart.</tr>
                                                     {/for}
                                                    </table>
                                                    {if customer.level == "gold"}
                                                      We love you!  Please check out our Gold Customer specials!
                                                    {else}
                                                      Become a Gold Customer by buying more stuff here.
                                                    {/if}
                                                

                                                Результат:
                                                Hello John Public.<br/>
                                                    Your shopping cart has 3 item(s):
                                                    <table>
                                                     <tr><td>Name</td><td>Description</td>
                                                         <td>Price</td><td>Quantity & Alert</td></tr>
                                                         <tr><td>MAC</td><td>computer</td>
                                                             <td>$1000</td><td>100 : </td>
                                                             </tr>
                                                         <tr><td>IPOD</td><td>music player</td>
                                                             <td>$200</td><td>200 : ON SALE NOW!</td>
                                                             </tr>
                                                         <tr><td>CINEMA DISPLAY</td><td>screen</td>
                                                             <td>$800</td><td>300 : BEST DEAL!</td>
                                                             </tr>
                                                    </table>
                                                      We love you!  Please check out our Gold Customer specials!
                                                

                                                  0
                                                  То что не увидел:
                                                  1. Как в нём произвести наследование снизу вверх?
                                                  2. Как в нём подключить дочерний шаблон?
                                                  3. Как в нём определить контент блока родительского шаблона?

                                                  Собственно это то, для чего я и делал шаблонизатор. В вашем варианте — не вижу.

                                                  То о чём я писал:
                                                  1. Свой синтаксис логики.
                                                  2. Текстовые преобразования в коде шаблона.

                                                  Складывается впечатление, что Вы не прочитали зачем я делал JUST.
                                                  0
                                                  Извините за оффтоп, но так хочется чтоб синтаксис шаблонов был стандартизирован.
                                                    –1
                                                    Есть интересный и простой рецепт: не используйте шаблонизаторы вообще. Пишите шаблоны на том же языке, что и весь проект, просто для удобства можно оформлять их отдельными файлами.
                                                    Сам я не сторонник такого подхода, но он точно лучше чем плодить новые шаблонизаторы.
                                                    0
                                                    По поводу экранирования средствами шаблонизатора. Я считаю, что шаблонизаторами не поддерживающими экранирование нельзя пользоваться из соображений безопасности, и MVC тут не при чём. Дело вот в чём. Если программисту нужно для вывода переменной в шаблон вручную вызывать всякие escape-функции, то он гарантированно будет забывать это делать, что будет приводить к дырам в безопасности. Это факт вызванный чисто психологическими причинами, и с ним ничего поделать нельзя. А вот если шаблонизатор предоставляет разные виды блоков для подстановки значений экранированных по разным правилам (обязательный минимум: html, url, без экранирования), то проблема безопасности решается очень просто: во-первых все привыкают использовать блок автоматически экранирующий html для 99% переменных; во-вторых любое использование блока не экранирующего вставляемое значение должно быть крайне редким и все такие блоки легко найти grep-ом по исходникам и проверить на безопасность. Обычно такие блоки (без экранирования) нужны для встраивания других шаблонов, но для этого как правило есть специализированные блоки, так что в большинстве проектов может вообще не быть ни одного блока без экранирования.

                                                    Другой интересный момент, которого зачастую нет во многих шаблонизаторах — адекватные сообщения об ошибках в шаблоне, с корректными номерами строк.
                                                      0
                                                      Это не обязательно делать вручную для каждого наора данных. Ничто не мешает создать прослойку перед шаблонизатором, которая будет подготавливать данные и кешировать их. По удобству написания будет практически точно так же, но не будет необходимости гонять одни и те же преобразования тысячи раз для одних и тех же данных.

                                                      По поводу второго момента. Этот функционал в JUST есть. Если ошибка возникает на этапе разбора шаблона (синтаксическая ошибка типа незакрытого блока) будет указан только файл. Если ошибка произойдёт на этапе выполнения (обращение к неопределённой переменной) то будет указан и файл и строка.
                                                        0
                                                        Я не понял Ваш ответ, такое ощущение что мы говорим об абсолютно разных вещах. Кеширование здесь вообще не при чём. Прослойка перед шаблонизатором не может знать какие данные куда вставляются (в html, в url, etc.) и, соответственно, по каким правилам эти данные надо экранировать. Откуда взялись тысячи преобразований одних и тех же данных я вообще не уловил. Попытаюсь кратко донести свою мысль ещё раз.

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

                                                        Поскольку в html-шаблонах обычно используется несколько типов данных (html, url, javascript, css), в каждом из них свои правила экранирования, и мы не можем автоматически определить куда именно вставляется это конкретное значение, то было бы удобно в языке шаблона (т.е. в синтаксисе «блоков» а-ля <%-… %> и т.п.) предусмотреть разные виды блоков отличающиеся правилами автоматически применяемого экранирования вставляемого значения. Простой пример такого шаблона:

                                                        <a href="/path?var=<%% some.value %>"> <%& some.othervalue %> </a>

                                                        здесь блок <%%…%> экранирует вставляемое значение по правилам экранирования для url (т.е. в %xx), а блок <%&…%> экранирует вставляемое значение по правилам экранирования для html (т.е. в &xxx;).
                                                          0
                                                          Вашу мысль я понял сразу. Логика тут есть, согласен, но это не отменяет того что я написал про огромное количество лишних строковых операций. Вот пример:

                                                          У нас есть страница с новостями, на ней кроме новостей который добавляются относительно редко, есть блок с комментариями пользователей, который обновляется очень быстро. Значит страницу мы перестраиваем очень часто.

                                                          Все новости у нас закешированы. Мы не берём из из БД, а сразу отправляем в шаблон. У новостей есть текст, который надо экранировать, url и другие детали требующие дополнительной обработки перед выводом.

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

                                                          Я предлагаю использовать прослойку. При добавлении новости она будет ложиться в кеш после обработки своей функцией подготовки к выводу. Достаточно одной такой функции для того чтобы выводить все новости на сайте, поэтому это не очень трудно для разработчика. Не нужно писать экранирования в каждом контроллере перед выводом. Нужно просто прогнать соответствующую сущность через её обработчик. В этом случае на все реквесты шаблонизатор будет отвечать уже готовыми данными и ему не потребуется каждый раз их преобразовывать.
                                                            0
                                                            Premature optimization. Я не уверен, что в природе есть сайты, в которых узким местом является экранирование вставляемых в шаблон данных. Даже если такие есть, они точно не являются типичным пользователем Вашего шаблонизатора, так что делать заранее оптимизации которые пригодятся 0.01% пользователей смысла мало. Кроме того, если речь заходит об оптимизации таких мелких деталей, то скорее всего новости будут кешироваться не в виде заэкранированных данных для подстановки в шаблон, а в виде уже отрендерённых в html кусков страницы, что позволит не только однократно экранировать данные но и однократно обрабатывать часть шаблонов из которых состоит страница. В общем, как обычно с premature optimization, если/когда дело дойдёт до реальной необходимости в оптимизации этого места кода, потребуются совершенно другие оптимизации.
                                                      0
                                                      github.com/mailru/Fest
                                                      пост закрыт
                                                        –1
                                                        Что это? Порт XSL на JavaScript? Жесть. XML для написания логики это наверное худшее что могли придумать в этой области. Я не могу даже представить причину по которой можно захотеть делать так. У нас есть проект с сотнями XSL шаблонов. Мы уже неоднократно задумывались переписать их на PHP+HTML и останавливает это сделать, только несоизмеримые затраты по времени. Ладно хотя бы не заставляют данные на вход в XML отдавать. Хочу это развидеть.
                                                          +2
                                                          у вас php на бэкенде — у нас — не только, у вас самописный парсер и дорогие строковые операции, у нас — быстрый SAX. Продолжать?
                                                            +1
                                                            В PHP+XSL напрягает не столько скорость (которая конечно не очень радует, но более-менее), а синтаксические излишки при реализации логики через XML.

                                                            То что бекэнды на разных языках это конечно создаёт трудности, но это не повод так усложнять разработку шаблонов. Хотя я прекрасно понимаю, что так может быть потому, что так уже давно и с этим сложно что-то поделать.

                                                            Не совсем понял про самописный парсер? Это Вы о JUST? Если да, тогда чем же он отличается от fest? Там тоже полно строковых операций. И да, шаблон в node.js разбирается только 1 раз при инициализации приложения и будет он разбираться 20мс или 40мс особой разницы нет. Выполняются они все примерно одинаково за 1-2мс (сейчас я говорю о тестах на слабенькой VPS).

                                                            Единственная причина портировать XSL на JavaScript тут может быть поддержка огромного количества уже написанных шаблонов, которые переписать не представляется возможным за разумное время.
                                                              +1
                                                              Сейчас уже поздно, к сожалению, вести осмысленную дискуссию, поэтому, предлагаю завтра продолжить. Про самописный парсер — я имел в виду парсинг и разбор самого шаблона. SAX в этом плане куда быстрее и легче собственных правил разбора тегов шаблона
                                                                +1
                                                                Насколько я вижу по исходнику sax.js это обычный конечный автомат. Один проход по тексту шаблона с переключением состояний. В JUST шаблон разбирается тоже одним проходом по тексту, но поскольку для реализации логики используется нативный JavaScript нет необходимости вычленять из шаблона управляющие конструкции, поэтому вместо обработки состояний достаточно реакции на 6 ключевых символов. Количество различных состояний в sax.js больше. Подход похож, но из за того, что синтаксис XML более строгий sax.js выполняет больше действий по похожему алгоритму и я, как минимум, сомневаюсь что она работает быстрее. Есть мнение что он будет медленнее разбирать шаблон чем парсер JUST. Но тут могут дать ответ только тесты. Результат в любом случае будет очень близок. И даже если кто-то из них победит, в node.js это время разбора роли не играет т.к. происходит 1 раз при загрузке шаблона.

                                                                При всём этом, насколько я вижу кешировать функции разработчику придётся самому, что раздует код приложения. В JUST это инкапсулировано. Также я не увидел в Fest возможность наследования шаблонов снизу вверх. Такой подход очень сильно облегчает разработку шаблонов и без него я шаблонизатор не рассматриваю в принципе. Хотя может там я его просто не заметил.
                                                                  0
                                                                  Наследование есть. Внимательно посмотрите тесты.
                                                                    0
                                                                    Просмотрел все тесты наследования не нашёл. Покажите?
                                                                    Вижу только инъекции github.com/mailru/fest/blob/master/tests/templates/include.xml
                                                                        0
                                                                        Либо я не вижу в этом примере как работает то, о чём я говорю, либо мы говорим о разных вещах. Я имею ввиду вот такой функционал:

                                                                        Есть 3 файла:

                                                                        layout.html
                                                                        <html>
                                                                            <head><title><%- title %></title></head>
                                                                            <body>
                                                                            <%*%>
                                                                            </body>
                                                                        </html>
                                                                        

                                                                        sublayout.html
                                                                        <%! layout %>
                                                                        <div>
                                                                            <h1><%- sectionTitle %></h1>
                                                                            <div><%*%></div>
                                                                        </div>
                                                                        

                                                                        page.html
                                                                        <%! sublayout %>
                                                                        <h2><%- pageTitle %></h2>
                                                                        <p><%- content %></p>
                                                                        

                                                                        Запустив рендеринг page.html с параметрами:
                                                                        { title: 'My Site', sectionTitle: 'My Section', pageTitle: 'My Page', content: 'My Content'}
                                                                        

                                                                        на выходе я получу HTML

                                                                        <html>
                                                                            <head><title>My Site</title></head>
                                                                            <body>
                                                                                <div>
                                                                                    <h1>My Section</h1>
                                                                                    <div>
                                                                                        <h2>My Page</h2>
                                                                                        <p>My Content</p>
                                                                                    </div>
                                                                                </div>
                                                                            </body>
                                                                        </html>
                                                                        

                                                                          0
                                                                          у нас больше похоже на apply-templates и template
                                                            0
                                                            А зачем переписывать XSL шаблоны, на, о боже, PHP+HTML? Из соображений производительности? Я понимаю, конечно, что многим громоздкие XML шаблоны не по душе, но менять их на такой беспросветный ужас как php-templates… В чём смысл?
                                                              0
                                                              Ну, если Вы считаете PHP-templates беспросветным ужасом это всего лишь Ваше субъективное мнение. Я, например, так же отношусь к XSL. PHP-templates это отличное решение, если php в них использовать только для шаблонизации, а не для написания костылей, в совокупности с тем, что на php это самый быстрый способ в силу своей нативности.
                                                                0
                                                                Я не спорю, что он быстрый. Но ведь от одного взгляда на типичный пхп-шаблон можно двинуть ноги. Это же нечитаемая каша невесть чего. А у XSLT посути всего два минуса — функционал слабоватый, громоздкий. Зато он предельно понятный и практически идеально вписывается в MVC. Впрочем дело ваше :) С каждым новым вашим комментарием я нахожу вас всё большим и большим мазохистом :)
                                                              0
                                                              У нас среднее время трансформации на списке при нагрузочных тестах писем меньше 2ms.
                                                              Возможность использовать все возможности JavaScript в шаблона на сервере.

                                                              XSL и по скорости и по возможностям проигрывает, хоть я его и очень сильно люблю.
                                                                0
                                                                Под трансформацией Вы имеете ввиду работу уже готовой функции для генерации результата или разбор шаблона для построения функции. Если первое, то JUST тоже показывает результаты в пределах 1-2мс. Но тут абсолютными показателями говорить бессмысленно, т.к. нужно сравнивать одинаковые шаблоны на одинаковом железе. Я посмотрел код fast. Крайне сомневаюсь, что он будет работать быстрее JUST. Как нибудь на досуге сравню.

                                                                Про XSL это был сарказм. Просто синтаксис шаблонов очень похож. Не знаю зачем могло понадобиться делать его таким. XSL-like конструкции слишком громоздкие.
                                                                  0
                                                                  Если JUST будет быстрее, надо будет понять почему и сделать так у себя.

                                                                  Про шаблонизатор, главная задача была научиться готовить JS на сервере.
                                                                  Поэтому взяли 100% проверенный фомат. Я чуть позже напишу очень большой пост про fest там будут все за и против.
                                                                    0
                                                                    Там проход по шаблону реализован одинаково практически. Только из за того что в xsl-like синтаксисе нужно парсить логику необходимо применение состояний для разбора открывающих и закрывающих тегов. А в JUST это не делается т.к. логика реализована через inline JavaScript. Там достаточно отреагировать на несколько ключевых символов и перескочить до следующего вхождения управляющей конструкции. Но это теоретически конечно. Нужны тесты.
                                                                      0
                                                                      Посмотрите в плане оптимизации doT. На порядок быстрее аналогов. Сравнил на скорую руку с just, тоже 5-10 раз.
                                                                        0
                                                                        Интересно было бы взглянуть на код тестов. Что именно сравнивали? Время компиляции, время выполнения или всё вместе?
                                                                          0
                                                                          Не сохранил тесты. Помню с ejs сравнивал just, один в один по скорости. Так вот тест ejs vs dot остался: dumpz.org/213153/
                                                                          Сейчас запустил: 1376 (ejs) против 164 (dot).

                                                                          Там на сайте dot бенчмарки есть браузерные и в репе для ноды вроде. Дописали бы туда just. Кстати, его женщина написала, dot :)
                                                                            0
                                                                            Посмотрел код dot. Да, там просто нечему тормозить. По сути, он заменяет управляющие символы на конструкции конкатенации строк и имеет обёртки для эскейпинга и определения подшаблона (внутри этого же шаблона). JUST разрабатывался для более широких задач. Самое главное это наследование и подшаблоны. Именно отдельные шаблоны (в своих файлах), а не отдельный кусок внутри одного файла. Для этого разбор и скомпилированную функцию пришлось сделать немного сложнее. Но без этого функционала шаблонизатор мне в принципе не нужен. Скорость разбора в node.js совсем не критична, т.к. шаблон разбирается только 1 раз после запуска приложения. Скорость работы — да будет помедленнее, т.к. результирующая функция это не просто конкатенация строк, а сбор массива и join его частей. Но у меня на реальном проекте время работы этой функции не превышает 1-2мс на слабеньком VPS (1CPU 1.2Ghz, 1Gb RAM). На данный момент я считаю этот показатель очень хорошим и от оптимизации перешёл к улучшению функционала. Добавил конструкции, для упрощения итерации объектов и описания условий.
                                                                              0
                                                                              Было бы ещё хорошо, если бы эта мультифайловость не сказывалась на тестах, где она не используется.

                                                                              По поводу синтаксиса. Думаю, будь он ejs совместимым, это сказалось бы на популярности в плюс.

                                                                              И ещё раз про эскейпинг. Это скорее вопрос безопасности, чем идеологии. Если по умолчанию заэскейпится лишняя переменная, вы заметите это при тестах. А вот если случайно не заэскейпится что-то, вы в браузере это визуально не заметите. Потенциальная дыра. Вот представьте, человек поддерживает два проекта в одном ejs в другом just. Синтаксис похож и забыть про тонкости эскейпинга можно запросто.
                                                            +1
                                                            очень жаль, что автор не добавил тесты производительности, интересно было бы сравнить с другими шаблонизаторами.
                                                              0
                                                              Согласен, что тестов не хватает. Я займусь этим как только появится время.
                                                            • UFO just landed and posted this here
                                                                +1
                                                                Высокая скорость работы

                                                                Серьезно? Как оценивали, мерили, с чем сравнивали?
                                                                Код далек от идеала, полно перлов, например:
                                                                html.slice(i, openLength + i)
                                                                // =>
                                                                html.substring(i, openLength)
                                                                
                                                                html.substr(i, 1)
                                                                // вместо более быстрого
                                                                html.charAt(i)
                                                                
                                                                while (~(n = js.indexOf('\n', n))) n++, lineNo++;
                                                                // =>
                                                                lineNo += js.split('\n').length - 1;
                                                                
                                                                .replace(/^\s\s*/, '').replace(/\s\s*$/, '')
                                                                // =>
                                                                .replace(/^\s+/, '').replace(/\s+$/, '')
                                                                // или если не заботят старые браузеры
                                                                .trim()
                                                                
                                                                new Function([], buffer);
                                                                // =>
                                                                new Function(buffer);
                                                                
                                                                blank.apply(that, []);
                                                                // =>
                                                                blank.call(that);
                                                                
                                                                Object.prototype.toString.call(options.root).slice(8, -1) == 'Object'
                                                                Object.prototype.toString.call(data).slice(8, -1) == 'String'
                                                                // вместо
                                                                typeof options.root == 'object'
                                                                typeof data == 'string'
                                                                
                                                                buffer[i] instanceof Array
                                                                // это не во всех браузерах будет работать
                                                                // в современных браузерах есть Array.isArray, для старых можно сделать эмуляцию, вот тут как раз нужен toString
                                                                if (typeof Array.isArray != 'function')
                                                                  Array.isArray = function(value){
                                                                    return Object.prototype.toString.call(value) === '[object Array]';
                                                                  }
                                                                ...
                                                                Array.isArray(buffer[i])
                                                                

                                                                Вообще же посимвольный разбор несколько медленно, у вас много операций со строками. Хотя фактически вы ищите начало и конец вставки. Лучше использовать регулярные выражения, чтобы получить все вхождения и тогда останется только пройтись по ним:
                                                                var rx = new RegExp(options.open + '((?:.|[\r\n])+?)(?:' + options.close + '|$)'); 
                                                                // тут надо позаботится чтобы в options.open/close не было управляющих символов
                                                                // или var rx = /<%((?:.|[\r\n])+?)(?:%>|$)/;
                                                                var matches = html.split(rx);
                                                                for (var i = 0, lineNo = 1, len = matches.length; i < len; i++)
                                                                {
                                                                  var text = matches[i];
                                                                  if (i & 1) // каждое нечетное вхождение это содержимое между options.open и options.close
                                                                  {
                                                                    var prefix, postfix, line = 'this.line=' + lineNo;
                                                                    var jsFromPos = 1;
                                                                    switch(text.charAt(0))
                                                                    {
                                                                      case '@':
                                                                        prefix = '\', (' + line + ', this.partial(\'';
                                                                	postfix = '\')), \'';
                                                                	break;
                                                                      //...
                                                                      default:
                                                                	prefix = '\');' + line + ';';
                                                                        postfix = '; this.buffer.push(\'
                                                                        jsFromPos = 0;
                                                                    }
                                                                    buffer.push(prefix, text.substr(jsFromPos).replace(/^\s+|\s+$/g, ''), text.postfix)
                                                                  }
                                                                  else       // каждое четное текст между вставками
                                                                  {
                                                                    buffer.push(
                                                                      text
                                                                        .replace(/[\\']/g, '\\$&')
                                                                        .replace(/\r/g, ' ')
                                                                        .replace(/\n/g, '\\n')
                                                                    );
                                                                  }
                                                                
                                                                  lineNo += text.split(/\n/).length - 1;
                                                                }
                                                                
                                                                  0
                                                                  Спасибо за дельные замечания. Проверю их и внесу соответствующие изменения. Регулярные выражения стоит протестировать на больших шаблонах и сравнить с посимвольным разбором. Скорость оценивал субъективно на работе конкретных шаблонов. Пока не было времени полноценно протестировать в сравнении, как только появится — сразу же этим займусь.
                                                                    0
                                                                    Проверил Ваш способ с разбором регулярными выражениями. На некоторых больших шаблонах прирост в скорости почти в 5 раз. Круто! Протестирую ещё различные комбинации по количеству управляющих конструкций в шаблоне и если всё будет так же радужно — заменю парсер. Спасибо за отличную идею.
                                                                      0
                                                                      Сам сталкивался с разбором на js не раз, и с более сложным… мой вывод что супер оптимизированный посимвольный разбор медленнее или наравне с плохо-оптимизированным решением на регулярных выражениях. В принципе это справедливо не только для js, но и для других высокоуровневых языков.
                                                                      На самом деле тут есть куда оптимизировать, сам алгоритм не очень удачный. Первое что нужно — это избавиться от лишних replace. К примеру, зачем trim'ить js? \r & \n можно заменять на \n, например так text.replace(/\r\n?|\n\r?/g, '\\n') — выработанный за годы regexp для таких случаев :)
                                                                      Так же стоит отказаться от вычисления lineNo — это минус split. Вместо lineNode считайте номер инъекции, или сохраняйте i. У вас же остается исходный код? Найти нужную строку можно потом, если ошибка возникнет. Например, так:
                                                                      ...
                                                                      catch(e){
                                                                        // var rx = new RegExp(options.open + '((?:.|[\r\n])+?)(?:' + options.close + '|$)'); 
                                                                        // rx у вас по идее должен один раз объявляться
                                                                        var lineNo = html.split(rx)
                                                                           .slice(0, this.partNo) // в partNo сохраняется i при разборе, вместо lineNo
                                                                           .join('')
                                                                           .split('\n')
                                                                           .length - 1;
                                                                        ...
                                                                      }
                                                                      

                                                                      Ошибки случаются редко, а в идеале — никогда. Поэтому если split перенесете из runtime в обработку ошибок, то можно еще выиграть на разборе.
                                                                        0
                                                                        Да, разбор можно ещё оптимизировать, но в целом он уже работает довольно быстро и по сути он не так критичен, т.к. для приложений на node.js и долгоживущих клиентских приложений парсится он всего один раз. Следующий этап будет оптимизация скорости выполнения самой функции генерации шаблона.

                                                                  Only users with full accounts can post comments. Log in, please.