Мобильные телефоны и гаджеты

Мобильные телефоны и гаджеты

» » Обзор актуальных шаблонизаторов JS. H Пишем простой шаблонизатор на js в черновиках Tutorial Зачем нужны шаблоны

Обзор актуальных шаблонизаторов JS. H Пишем простой шаблонизатор на js в черновиках Tutorial Зачем нужны шаблоны

Число JS-библиотек ни в коей мере не уменьшается; наоборот, оно растёт с каждым днём. Когда мы доходим до приложений JS, лучшим выбором оказываются шаблоны, чем полноценные библиотеки, потому что это приводит к более чистому базовому коду и лучшему процессу работы с ними .

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



Их список можно найти в Википедии , там отлично сравниваются движки для разных языков программирования для веба, но там действительно не фокусируются на одном языке, поэтому я хотел бы посмотреть, как много движков можно перечислить для Javascript.

Если вы разрабатываете на Javascript, то узнаете ряд движков, но можно узнать и о некоторых новых. Я с удовольствием продолжу этот список вместе, и надеюсь, что он вас порадует.

chibi.js

Chibi даёт всё для экономии траффика и времени для отображения шаблона, небольшая и лёгкая библиотека, которая поможет лучше шаблонизировать приложение. Больше фокусируется на CSS вместо использования анимации. (Дословная «вода» автора - перев.)

templayed.js

Это - самый маленький шаблонизатор, с которым вы столкнётесь, гарантированно (Nano - перев.) . Он построен на основе Mustache, прост в использовании и понимании. Сайт имеет большой демо-пример, в котором можно запускать и тестировать код.

ECT

Подобно templayed, ECT тоже имеет демо-страницы установки на сайте, с которыми можно «поиграться» и понаблюдать живые результаты. Он сделан для скорости, и заявляет о себе как о самом быстром шаблонизаторе для JS (построен на Coffeescript). Совместим с Node.js и имеет понятный синтаксис. На Github есть бенчмарки и модульные тесты , показывающие эффективность этой библиотеки.

Pithy.js

Имеется внутренний DSL для генерации HTML в JavaScript. Это отлично для небольших фронтенд-проектов, но не рекомендуется для тяжёлых HTML-страниц.

T.js

T.js использует простую структуру данных Javascript для представления данных HTML / XML.

Nunjucks

Созданный в Mozilla, Nunjucks сделан для нуждающихся в производительности и гибкости из-за возможности расширять пользовательскую библиотеку плагинов и функций.

Jade

Jade рассчитан прежде всего для серверных шаблонов в node.js, но может работать во многих других средах. Он сделан только для XML-подобных документов (HTML, RSS, ...), поэтому не используйте его для оформления простого текста, markdown, CSS и подобных документов.

Dust.js

Dust расширяет Mustache и предлагает высокое качество исполнения по сравнению с другими решениями из этого списка. Содержит очень простой и понятный API.

Движки шаблонизации Javascript Я не пытался приводить примеры, потому что множество ссылок на официальные страницы содержат демонстрации.

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

UPD : комментаторы статьи-оригинала добавили :

  • "Pure - Simple and ultra-fast templating tool to generate HTML from JSON data
  • Dust.js is used by PayPal, and default in their Kraken.js framework.
  • Swig - A simple, powerful, and extendable JavaScript Template Engine.
UPD2 : упомянуты в комментариях к этой статье :
  • Twig - JS implementation of the Twig Templating Language
  • EJS Embedded JavaScript templates for node

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

Собственно, если интересно, то под катом некоторые заметки и выводы по процессу создания.

Для тех, кому только глянуть: the result , the cat .

Исходный шаблон - это JS String(), а данные это JS Object().
Блоки вида {% name %} body {% / %} , возможна неограниченная вложенность .
Если значение name является списком , то выводятся все элементы, иначе если не undefined, выводится один элемент.
Подстановки вида: {{ name }} .
В блоках и подстановках возможно использование точек в качестве имени, например {{.}} или {%.%} , где точка будет текущим элементом объекта верхнего уровня.
Есть ещё комментарии - это {# any comment w\wo multiline #} .
Для самих значений возможны фильтры, задаются через двоеточие: {{ .:trim:capitalize… }} .

Работать оно должно как:

Var str = render (tpl, obj);

Доказать:
+1 к самооценке .

UPD 2 : Сразу скажу, чтобы «чётко понять» что там и зачем, нужно начать это делать, желательно вместе с debugger"ом.
UPD 3 : Оно разобрано «на пальцах». Там есть ещё где «оптимизнуть». Но будет гораздо менее наглядно.

Приступим.

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

Для начала можно убрать комментарии, чтобы не отсвечивали:

// to cut the comments tpl = tpl.replace (/\{#[^]*?#\}/g, "");

Hint: [^] означает любой символ, * - сколько угодно раз.

Теперь можно подумать над тем, как будем парсить «чистый» результат.
Так как блоки возможны вложенные, предлагаю хранить всё в виде дерева.
На каждом уровне дерева будет JS Array (), элементы которого могут содержать аналогичную структуру.

Чтобы создать этот массив нужно отделить мух от котлет.
Для этого я воспользовался String.split() и String.match() .

Ещё нам понадобится глубокий поиск по строковому val имени внутри объекта obj .

Применённый вариант getObjDeep:

var deeps = function (obj, val) { var hs = val.split("."); var len = hs.length; var deep; var num = 0; for (var i = 0; i < len; i++) { var el = hs[i]; if (deep) { if (deep) { deep = deep; num++; } } else { if (obj) { deep = obj; num++; } } } if (num == len) { return deep; } else { return undefined; } };

Итак, разделим строку на части parts и элементы matches:

// регулярка для парсинга: // цифробуквы, точка, подчеркивание, // двоеточие, слеш и минус, сколько угодно раз var ptn = /\{\%\s*+?\s*\%\}/g; // строковые куски var parts = tpl.split (ptn); // сами спички var matches = tpl.match (ptn);

Для разбора полётов нам понадобятся два массива.
В одном мы будем хранить блоки, в другом будет текущий элемент из цикла по спичкам.

// все блоки var blocks = ; // вложенности var curnt = ; if(matches){ // т.к. м.б. null var len = matches.length; for (var i = 0; i < len; i++) { // выкидываем {% и %}, и попутно делаем trim var str = matches[i].replace (/^\{\%\s*|\s*\%\}$/g, ""); if (str === "/") { // finalise block // ... } else { // make block // ... } // ...

Тут blocks - итоговый массив с выделенными блоками, а curnt - массив с текущей вложенностью.

На каждом шаге цикла мы определяем, что сейчас в str, начало блока или завершение.
Если начало блока, т.е. str !== "/" , то создаём новый элемент и push его в массив.
И ещё push его в curnt, т.к. нам необходимо понимать на каком мы уровне.
Попутно заносим в блок сами строки.
Соответственно, если у нас пустой curnt, то мы на нулевом уровне дерева.
Если curnt не пустой, то нужно заносить в nested элемент последнего curnt.

// длина текущей вложенности var cln = curnt.length; if (cln == 0) { // т.к. это верхний уровень, то просто в него и кладём текущий элемент blocks.push (struct); // пишем текущую вложенность, она же нулевая curnt.push (struct); } else { // нужно положить в nested текущего вложенного блока curnt.nest.push (struct); // теперь взять этот "последний" элемент и добавить его в curnt var last = curnt.nest.length - 1; curnt.push (curnt.nest [ last ]); }

Соотвественно, каждый элемент массива это, минимум:

Var struct = { // текущий obj для блока cnt: deeps(obj, str), // вложенные блоки nest: , // строка перед всеми вложенными блоками be4e: parts[ i + 1 ], // str -- строка, идущая после завершения данного // cnt -- блок-родитель, парсить строку будем в его рамках af3e: { cnt: null, str: "" } };

Т.к. у нас может быть ситуация, когда после блока есть что-нибудь ещё, то здесь af3e.str и должно быть строкой, идущей сразу после {% / %} текущего блока. Все необходимые ссылки мы проставим в момент завершения блока, так наглядней.
В этот же момент мы удаляем последний элемент элемент curnt.

If (str === "/") { // предыдущий элемент curnt // является родителем // завершившегося сейчас блока curnt .af3e = { cnt: (curnt [ cln - 2 ] ? curnt [ cln - 2 ].cnt: obj), str: parts[ i + 1 ] }; curnt.pop();

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

// массив строк для парсинга элементарных частей блоков var stars = [ [ parts, obj ] ]; parseBlocks(blocks, stars);

Примерный вид parseBlocks()

var parseBlocks = function (blocks, stars) { var len = blocks.length; for (var i = 0; i < len; i++) { var block = blocks [i]; // если определён текущий obj для блока if (block.cnt) { var current = block.cnt; // найдём списки switch (Object.prototype.toString.call(current)) { // если у нас массив case "": var len1 = current.length; for (var k = 0; k < len1; k++) { // кладём в stars текущий элемент массива и его строку stars.push ([ block.be4e, current[k] ]); // парсим вложенные блоки parseBlocks(block.nest, stars); } break; // если у нас объект case "": for (var k in current) { if (current.hasOwnProperty(k)) { // кладём в stars текущий элемент объекта и его строку stars.push ([ block.be4e, current[k] ]); // парсим вложенные блоки parseBlocks(block.nest, stars); } } break; // у нас не массив и не объект, просто выведем его default: stars.push ([ block.be4e, current ]); parseBlocks(block.nest, stars); } // кладём в stars то, что было после текущего блока stars.push ([ block.af3e.str, block.af3e.cnt ]); } } };

Var pstr = ; var len = stars.length; for (var i = 0; i < len; i++) { pstr.push(parseStar (stars[i], stars[i])); } // Результат: return pstr.join ("");

Примерный вид parseStar()

var parseStar = function (part, current) { var str = ""; // убираем лишнее var ptn = /\{\{\s*.+?\s*\}\}/g; var parts = part.split (ptn); var matches = part.match (ptn); // начинаем собирать строку str += parts; if (matches) { var len = matches.length; for (var i = 0; i < len; i++) { // текущий элемент со значением var match = matches [i]; // убираем лишнее и делаем trim var el = match.replace(/^\{\{\s*|\s*\}\}$/g, ""); var strel = ""; // находим элемент в текущем объекте var deep = deeps(current, el); // если нашли, то добавляем его к строке deep && (strel += deep); str += strel; } if (len > 0) { str += parts[ len ]; } } return str; }

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

Но моей целью было показать саму концепцию…

А результат, как уже было сказано в начале статьи, можно найти .
Итоговый пример .

Надеюсь, кому-нибудь пригодится.
Спасибо за внимание!

3.2 из 5

Mustache — шаблонизатор, который содержит минимум управляющей логики и доступен для разных языков программирования. Его можно использовать и на сервере (PHP, Ruby и т.д.), и на клиенте (Javascript).

Если вы динамически подгружали с сервера блоки HTML-кода, а не структурированные данные только потому, что не хотели дублировать рендеринг на сервере и на клиенте, то Mustache поможет этого избежать.

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

Рассмотрим простой пример шаблона:

{{header}}

{{content}}



    {{#authors}}
  • {{#accent}} {{.}} {{/accent}}

  • {{/authors}}
    {{^authors}}
  • anonymous

  • {{/authors}}

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

Var data = {
header: "Новый пост",
content: "Первая строка
Вторая",
authors: ["alex", "daemon", "john"],
accent: function () {
return function (text, render) {
text = render(text);
return "" + text + "";
}
}
};

Чтобы «запустить» шаблонизатор и отрисовать с помощью шаблона данные, необходимо подключить библиотеку:

И вызывать рендеринг методом to_html:

Mustache.to_html(template, data);

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

Более подробно о тегах

Всего в Mustache четыре основных типа тегов: переменная , секция , комментарий и подключение дополнительного шаблона .

Переменная выводит данные с экранированием HTML-сущностей {{header}} и без экранирования {{{content}}} . Отличаются они количеством скобок. В нашем случае, вместо {{header}} подставится строчка «Новый пост».

Секция представляет собой парный тег. Принцип ее действия зависит от типа данных, с которыми она работает. Если в контексте имени секции соответствует поле со списком, то шаблонизатор проходит по его элементам и текст внутри парного тега обрабатывается по одному разу для каждого элемента списка. Элемент списка подставляется заместо тега-точки. Так, например, секция {{#authors}}

  • {{.}}
  • {{/authors}} превратится в
  • alex
  • daemon
  • john
  • . Если список пуст, то обрабатывается содержимое «тега с крышечкой», в нашем случае — это {{^authors}} … {{/authors}} .

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

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

    Комментарий оформляется в виде тега с восклицательным знаком, например, {{! comment content}} .

    Подключение дополнительного шаблона вызывается с помощью тега с угловой скобкой. Например, {{>copyright}} . Если в текущем контексте присутствует поле с таким названием, то оно будет передано в качестве контекста для подключаемого шаблона.

    Производительность

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


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

    Но для тех кто не в теме немного о том что же это такое и что оно нам даст.

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

    И так мы хотим рендить шаблоны в 1 проход и иметь представление отдельно от логики. Обычно шаблонизаторы имеют подобный интерфейс.
    template.Run("template url", { VarName:"VarValue" });
    Ниже код с подробными комментариями (что и почему)
    window.template = { //заружает представление TemplateGet: function (TemplateUrl) { // 1. Создаём новый объект XMLHttpRequest var xhr = new XMLHttpRequest(); // 2. Конфигурируем его: GET-запрос на URL "TemplateUrl" xhr.open("GET", TemplateUrl, false); // 3. Отсылаем запрос xhr.send(); if (xhr.status >= 200 && xhr.status < 400) { // вернуть результат return xhr.responseText; } else { // обработать ошибку alert(xhr.status + ": " + xhr.statusText); // пример вывода: 404: Not Found return ""; } }, //класс функций для рендинга Render:{ //а в этом классе мы напишем все функции рендера func:{ //рендит переменные vars: function(html, vars){ //ищем все переменные в шаблоне var $ = html.match(/{{var.(.*?)}}/g); //проверяем нашли ли что то if (Array.isArray($)) { //мы нашли переменные в шаблоне. Необходимо их все распарсить. $.forEach(function (item) { //item содержит найденую строку {{var.VarName}} //по этому мы должны распарсить ету строку дабы получить только VarName item = item.replace("{{var.", ""); item = item.replace("}}", ""); //у нас есть имя переменной. Пора проверить передали ли мы такую //и обработать ошибки if (vars === undefined){ //перенную не передали. //пишем об ошибке в консоль и заменяем её значение в шаблоне на null console.warn("Переменная "+item+" не найдена"); html = html.replace("{{var." + item + "}}", "null"); } else { //переменную мы нашли. Вставим ка её в шаблон html = html.replace("{{var." + item + "}}", vars); } }); } //удалим обьект с переменными delete vars; //вернём html return html; } }, //точка входа в рендер. Run:function(html, vars){ html = this.func.vars(html, vars); return html; } }, //точка входа Run: function (TemplateUrl, vars) { //загружаем шаблон var html = this.TemplateGet(TemplateUrl); //отправляем шаблон рендеру html = this.Render.Run(html, vars); //удаляем обьект с переменными. Он нам больше не нужен, а может занимать много места delete vars; //результат шаблонизатор вставит в document.getElementById("page").innerHTML = html; } }; //вызовем представление template.Run("test.tpl", { VarName: "test" });
    А в test.tpl просто напишем
    {{var.VarName}}

    Я надеюсь теперь меньше станет jquery лапши)

    Недавно пришлось столкнуться с уже готовым очень простым проектом, в котором было необходимо добавить вывод дерева категорий и товаров в них. Данные приходили в виде json через запросы к API. В арсенале на фронте был лишь jQuery, а привыкнув к шикарному шаблонизатору Angular’а, я не хотел вновь возвращаться к конкатенации строк, и подключать какой-то фреймворк естественно не имело смысла. Мне необходим был минимальный шаблонизатор, который мог бы повторять куски верстки подобно директивам angular, с условиями и переменными.

    Вооружившись статьями авторов John Resig и Krasimir Tsonev , я приступил к работе. В итоге я получил функцию, которая могла компилировать шаблон типа:

    min

    Начнем с банального:

    Var TemplateEngine = function(tpl, data) { // код шаблонизатора } var template = "

    Hello, my name is . I\"m years old.

    "; console.log(TemplateEngine(template, { name: "John", age: 23 }));

    И как вы догадались, хотелось бы, что бы функция возвращала:

    Hello, my name is John. I"m 23 years old.

    Для обнаружения js переменных в тексте верстки воспользуемся регулярным выражением:

    Var re = /]+)?%>/g

    С помощью данной регулярки мы сможем находить все что находится между тегами . Параметр /g обозначает, что нас интересует не одно совпадение, а все.

    Есть множество способов как использовать в js регулярное выражение, мы воспользуемся методом.exec():

    Var re = /]+)?%>/g; var match = re.exec(tpl);

    Если мы сделаем console.log переменной match, то получим следующее:

    [ "", " name ", index: 21, input: "

    Hello, my name is . I\"m years old.

    " ]

    Как вы видите, наш масcив содержит лишь один элемент, а обработать нам нужно все, поэтому обернем всю логику в цикл while.

    Var re = /]+)?%>/g, match; while(match = re.exec(tpl)) { console.log(match); }

    Запустив данный код мы найдем обе переменные и .

    Теперь самое интересное, нам необходимо заменить найденные переменные их значениями. Самое простое что приходит на ум это сделать простой.replace(). Но это бы работало с простыми json объектами с одним уровнем вложенности. На практике же мы имеем дело с объектами, которые имеют многоуровневую вложенность:

    { name: "John", profile: { age: 23 } }

    И tpl.replace(match, data) будет уже не достаточным решением. Потому что когда мы напишем , код заменится на data[«profile.age»] и будет undefined. Так как способ замены нам не подходит, было бы очень круто, если бы между тегами можно было выполнять реальный js код.

    Var template = "

    Hello, my name is . I\"m years old.

    ";

    Как избавиться от this я расскажу ниже.

    Как же это реализовать? В статье Джона Резига он использует new Function для создания функции из строки.

    Var fn = new Function("arg", "console.log(arg + 1);"); fn(2); // outputs 3

    Для понимания, данный код можно рассмотреть как:

    Var fn = function(arg) { console.log(arg + 1); } fn(2); // outputs 3

    fn — реальная функция, которая выполняет другую функцию, которая передана как текстовый параметр.

    Это именно то, что нам необходимо, нам необходимо шаблон преобразовывать в вид:

    Return "

    Hello, my name is " + this.name + ". I\"m " + this.profile.age + " years old.

    ";

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

    Return "My skills:" + for(var index in this.skills) { + "" + this.skills + "" + }

    мы конечно будем хватать ошибки. Именно для решения этой задачи Джон разбивает строку на элементы массива а в конце объединяет в строку.

    Var r = ; r.push("My skills:"); for(var index in this.skills) { r.push(""); } return r.join("");

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

    Var TemplateEngine = function(tpl, data) { var re = /]+)?%>/g, code = "var r=;\n", cursor = 0, match; var add = function(line) { code += "r.push("" + line.replace(/"/g, "\\"") + "");\n"; } while(match = re.exec(tpl)) { add(tpl.slice(cursor, match.index)); add(match); cursor = match.index + match.length; } add(tpl.substr(cursor, tpl.length - cursor)); code += "return r.join("");"; // /g, reExp = /(^()?(var|if|for|else|switch|case|break|{|}|;))(?:(?=\()|(?=)|$)/g, code = "var r=;\n", cursor = 0, match; var add = function(line, js) { js? (code += line.match(reExp) ? line + "\n" : "r.push(" + line + ");\n") : (code += line != "" ? "r.push("" + line.replace(/"/g, "\\"") + "");\n" : ""); return add; };

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

    Var r=; r.push("My skills:"); for(var index in this.skills) { r.push(""); r.push(this.skills); r.push(""); } r.push(""); return r.join("");

    И конечно же все это будет успешно скомпилировано.

    Теперь для того, что бы шаблон не передавать строкой, а удобно его верстать в теле html страницы, мы поместим его между тегами:

    В данном случае mime тип text/html для браузера будет неизвестным и он пропустит выполнение его. Но содержимое этих тегов мы легко можем получить с помощью.innerHTML.

    Добавим теперь в начало нашего шаблонизатора проверку, если строка начинается с #, то это id нашего шаблона.

    Var html = tpl.charAt(0) === "#" ? document.getElementById(tpl.substring(1)).innerHTML: tpl;

    Если строка начинается не с #, значит мы передали сразу шаблон как строку.

    И завершающим этапом для нас будет избавление от обращения к this перед каждой переменной, а для этого мы передадим имена переменных scope в параметре new Function, а значения их мы присвоим с помощью метода.apply().

    Return new Function(name, code.replace(/[\r\t\n]/g, "")).apply(this,value);

    Теперь наш шаблонизатор может принять шаблон типа:

    min

    при этом js будет выглядеть так:

    Var scope = [ { name: "item 1", price: "10$", time: "30" }, { name: "item 1", price: "10$", time: "30" }, { name: "item 1", price: "10$", time: "30" } ]; var template = tpl("#tpl_id",scope);

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

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

    Var tpl = function (str, data) { var name = , value = ; var html = str.charAt(0) === "#" ? document.getElementById(str.substring(1)).innerHTML: str; if (typeof(data) === "object") { for (var k in data) { name.push(k); value.push(data[k]); } } var re = /]+)?%>/g, reExp = /(^()?(var|if|for|else|switch|case|break|{|}|;))(?:(?=\()|(?=)|$)/g, code = "var r=;\n", cursor = 0, match; var add = function(line, js) { js? (code += line.match(reExp) ? line + "\n" : "r.push(" + line + ");\n") : (code += line != "" ? "r.push("" + line.replace(/"/g, "\\"") + "");\n" : ""); return add; }; while(match = re.exec(html)) { add(html.slice(cursor, match.index))(match, true); cursor = match.index + match.length; } add(html.substr(cursor, html.length - cursor)); code += "return r.join("");"; return new Function(name, code.replace(/[\r\t\n]/g, "")).apply(this,value); };