String.format('N: #{a} : #{b}',{a:1,b:2})
.with(){}
and eval/new Function('')
also did not please. Thinking that "I need something just a little bit" and decided to write for myself. Thus were born two tags list
and visible
and the format of the form #{a==1?1:-1}
. It was not enough for me to only find these tags, then well String.format
. And now, for a year and a half, this engine carried its service perfectly - it was faster and faster, more reliable and more reliable.{tagName:'someTagName', attr: { key:'value'}, nodes:[] }
and Literal = {content:'Some String'}
. And since after 1.5 years of using the old template engine, I never remember that I would substitute template data in the name of the tag, or attribute name, for simplicity of the template and analyzer, we make it possible to insert data only into them. Therefore, the nodes will be of the following form: Tag: = {tagName:'name', attr: { key:('value'||function)}, nodes:[] }
and Literal: = {content:('Some String'||function)}
. Where function
is a function that substitutes the template data and it is only for those values ​​that require them. So we planted a tree, so far nothing complicated ( further it will not be more difficult either ).var T = {index:0 /* currentIndex */, length:template.length, template:template}
. When calling additional functions, we pass it (or rather, a link to it is passed). Thus the template string
not copied. var current = T; for (; T.index < T.length; T.index++) { var c = T.template.charCodeAt(T.index); switch (c) { case 32: //" " continue; case 39: // "'" T.index++; var content = T.sliceToChar("'"); // sliceToChar indexOf 'escape character' // . indexOf , , charCodeAt/charAt/[] if (~content.indexOf('#{')) content = T.serialize == null ? this.toFunction(content) : { template: content }; current.nodes.push({ content: content }); if (current.__single) { // , , ; div > ul > li > span > 'Some' if (current == null) continue; do (current = current.parent) while (current != null && current.__single != null); } continue; case 62: /* '>' */ current.__single = true; continue; case 123: /* '{' */ continue; case 59: /* ';' */ case 125: /* '}' */ if (current == null) continue; // ; , } - do(current = current.parent) while (current != null && current.__single != null); continue; } // - tag var start = T.index; do(c = T.template.charCodeAt(++T.index)) while (c !== 32 && c !== 35 && c !== 46 && c !== 59 && c !== 123); /** while !: ' ', # , . , ; , { */ var tag = { tagName: T.template.substring(start, T.index), parent: current }; current.nodes.push(tag); current = tag; this.parseAttributes(T, current); // ; > {, T.index--; }
html string
to insert into a document, you need to build a documentFragment
right away (although function renderHtml
left the function renderHtml
too, just in case) . With this we greatly compensate the time spent on parsing. function buildDom(node, values, container) { if (container == null) container = document.createDocumentFragment(); if (node instanceof Array) { for (var i = 0, length = node.length; i < length; i++) buildDom(node[i], values, container); return container; } if (CustomTags.all[node.tagName] != null) { var custom = new CustomTags.all[node.tagName](); for (var key in node) custom[key] = node[key]; custom.render(values, container); return container; } if (node.content != null) { // container.appendChild(document.createTextNode(typeof node.content === 'function' ? node.content(values) : node.content)); return container; } var tag = document.createElement(node.tagName); for (var key in node.attr) { var value = typeof node.attr[key] == 'function' ? node.attr[key](values) : node.attr[key]; if (value) tag.setAttribute(key, value); } if (node.nodes != null) { buildDom(node.nodes, values, tag); } container.appendChild(tag); return container; }
shallow copy
values ​​of attr
and nodes
and pass the assembly context to the render function. That is, our control should implement in its prototype the function .render(currentValues, container)
#{obj.other.value}
#{fnName:line}
. If fnName is an empty line, it is assumed that line is a condition and will be executed by the ValueUtilities.condition(line, values)
function ValueUtilities.condition(line, values)
Source: https://habr.com/ru/post/149509/
All Articles