Перед тем как приступить к подробному разбору полётов, сразу взглянем на пример использования, который скорей нагляднее отразит суть, чем что-либо ещё. Код создания сервера приводить не буду, просто опишу сам шаблон и парсинг под определённым контекстом.
HTML-шабон:
<html> <head> <title>#{PageTitle}</title> </head> <body> <h1>#{PageTitle}</h1> <ul> <# for (var i=0; i<List.length; i++) { #> <li> <# if (typeof List[i] !== 'object') { show(List[i]); } else { show(List[i].name +' - '+ List[i].note); } #> </li> <# } #> </ul> </body> </html>
Контекст:
var context = { PageTitle: 'jJSt test', List: ['First', {name:'Second', note:'2th'}, 'Third'], }; var result = njst.parse(html, context, {debug:1});
Идея
Я думаю мне стоит уделить пару слов о том, как я
В предыдущих попытках вылить эту идею в жизнь — я применял следующую конструкцию шаблонных вставок:
<script type="nodejs">...</script>Это было хорошо тем, что гарантировало подсветку JS-кода практически в любом редакторе. Плюс это напоминает о том, что код вставки является тем же JavaScript-ом. Но я быстро от этого ушёл, когда составил трезвый список недостатков такого приёма:
- Много символов, получаются слишком длинные вставки, когда нужно вывести всего-то одно поле;
- Такие вставки будут визуально путаться верстальщиком с клиентским JavaScript;
- Необходимость подсветки сама себя исчерпала, ведь в идеале код вставок должен быть минимальным и подсветка там особо даже и не к чему;
<% if (true) { &> html <& } %>То-бишь для вывода чистого HTML использовались не %> и <%, а &> и <&. И эта подсветка быстро стала проблемой. Я выбрал вариант с решёткой <# if (true) { &> html <& } #>. А спустя время я изменил принцип парсинга и исклчил заморочки с <&>. И в итоге всё стало похоже на аналогичные PHP вставки (кстасти шаблонизтор приближен к его принципу вставок кода). А вот менять решётку на символ процента (<% if (true) { %> html <% } %>) я как-то и не стал. В прочем если считаете, что зря — скажите мне об этом.
Вот вставки, которые доступны в применению:
- #{variable} — для вывода variable, переданной в контексте;
- <# jsCode #> — для выполнения JS-кода, циклов, условий и прочего. Так же возможен и вывод с помощью функции show().
Спецификация
Теперь поговорим о рекомендациях к применению. Так как тут особо ограничений нет, нужно задать тон, ибо потенциал говнокодинга вполне себе велик.
Во-первых: в #{...} — желательно писать только название ключа из контекста, чтобы вывести конкретное значение, либо имя раннее заданной переменной, никаких лишних операций, пусть и некоторые из них возможны. Ну, например, собрать массив в строку: #{arr.join(', ')} — я думаю будет не великим грехом. Переводы строк и ; здесь намеренно игнорируются. Для всяческого кода (циклов, условий и прочего) использовать <# ... #>.
Если вам нужен вывод внутри вставки <# ... #>, то на помощь вам придёт функция show() или переменная toShow, которую можно наполнить (но не заменить, иначе предыдущий вывод, в т.ч. от show() будет затёрт). Вот пример вывода с использованием этих инструментов:
<# while (toShow.length < 100) { show('I like it! '); toShow += 'I love it! '; } #>
Во-вторых: простое правило в теории, а в реалии про него часто забывают: никакого лишнего кодинга! Только необходимое для вывода, думаю циклов и условий хватит за глаза. Функции хоть и можно создавать, но всегда помните, что это — смертный грех, и если будете так делать — у вас завянуть пальцы, и вы больше не сможете писать функции в шаблонах.
В-третьих: если вы незнакомы со спецификацией JavaScript (пусть и неофициальной), то обязательно начните с неё, читать тут. Если с вашим кодом в потенциале могут работать другие люди, или вы сами спустя какое-то в время, надеясь потом понять самого себя, собираетесь с ним работать, то не избегайте этого пункта. Это очень популярная проблема, я и сам болел, но после того как побывал в роли жертвы, понял как важен этот момент. Иной раз встретишь такое, что мозг никак не может интерпретировать, да и сам автор без психотропных средств с трудом справится.
Область видимости
Один из вопросов, который скорее остальных может промелькнуть у вас в голове: «а если задать переменную var v = true; будет ли она видна в следующей шаблонной вставке вставке?» — ответ — ДА! И если вам трудно понять «как это работает», то хорошо, если вы знаете PHP, представьте, что вставки шаблонизатора nJSt — это вставки PHP, поведение наиболее приближенно именно к такому типу. Так же как и вывод:
#{...} = <?=...?> <# ... #> = <? ... ?>
Безопасность
Как и говорилось выше, внутрь шаблона попадает только контекст. Никаких глобальных объектов (require, global). Перезаписать какую-нибудь внешнюю глобальную переменную тоже не получится. В общем всё в рамках контекста. Конечно, если есть нужда работать с наружностями — передаём нужную функцию в контексте, которая по запросу будет это делать.
Не скрою, прорехи всё-таки есть, но в следующих версиях я займусь их прикрытием. Например, если написать <# while(true)++ #> — мы получим 100% нагрузки на процессор, и пока не вызвать kill по процессу node, можно использовать компьютер как обогреватель, а дальнейшие действия в ноде становятся невозможны. В следующей версии я собираюсь повесить таймер и убивать выполнение, если оно затягивается во избежании таких вот зависаний сервера.
Возможности и применение
Возможности скорей ограничиваются лишь вашей фантазией, поскольку в контекст мы передаём что хотим. На GitHub в примерах есть демо вызова функции для скачивания текущей страницы. Функция скачивания передаётся в контекст и вызывается в шаблоне. Вы можете передать любую функцию в контексте, которая является инструментом вашего проекта, и которая, может быть, оперирует внешней средой. В общем, если вы знаете JS, то не должно возникнуть никаких проблем. А если не знаете, то вот раз и два.
В следующих версиях
- Защита от зацикливаний (см. безопасность);
- Возможность возврата изменённого в шаблоне контекста от парсера.
Пользуйтесь на здоровье
Если хотите больше примеров и поподробнее, то см. папку examples на GitHub, там же и качайте сам модуль и изучайте исходные коды.
nJSt на GitHub: https://github.com/unclechu/njst
Жду ваших отзывов и пожеланий по доработке.
а как это дело правильно прикрутить к express?
ОтветитьУдалитьКачаем с GitHub-а папку njst в корень проекта и вот пример:
ОтветитьУдалитьvar app = express.createServer();
var njst = require('./njst');
app.get('/', function(req, res){
res.send(njst.parse('bla - #{test} - bla', {test:'olala'}, {debug:1}));
});
app.listen(3000);