Front
JS template engine has a lot of, I used to use art-template, and sometimes take Vue to use as a template engine.
Until......
At the beginning of the year, I was in the last project group, when the code specification was not allowed to use "external code", embarrassing.
If there is a need, then write it, but later for some reason useless. Later divided the production line, I built a set of construction, with a few months feel quite cool, the small code in accordance with the norms of the public to rewrite, to share with you.
Https://github.com/shalldie/mini-tpl
Grammar
The first is to choose the template syntax, EJS syntax is preferred, because the public, more than the need to learn the directive template engine of those things.
If you write a JSP or asp/asp.net, you can get started directly.
How do you use it?
I'm going to use this.
<Body> <DivID= "root"></Div> <ScriptID= "Tplcontent"type= "text/html"> <ul> <% for(varI=0; I<data.length; I++){ varItem=Data[i]; if(Item.age< -){%> <Li>my name is.<%=Item.name%>, my age is<%=Item.age%></li> <%}Else{%> <Li>My name is<%=Item.name%>, my age is a sercet.</li> <%}%> <% } %> </ul> </Script> <Scriptsrc=".. /build/mini-tpl.min.js "></Script> <Script> varData=[{name:'Tom', Age: A}, {name:'Lily', Age: -}, {name:'Lucy', Age: - }]; varcontent=document.getElementById ('tplcontent'). InnerHTML; varresult=minitpl (content, data); document.getElementById ('Root'). InnerHTML=result; </Script> </Body>
If you want to use this, then analyze how it can be done.
New Function
Const CONTENT = ' Console.log ("Hello World"); ' ; 2 3 New Function (content); 4 5 // Hello World
new Function ([arg1[, arg2[, ...argN]],] functionBody)
functionbody A containing function definition JavaScript语句的字符串
.
Functions generated using the function constructor do not create closures in the context in which they are created; they are typically created in the global scope.
When these functions are run, they can only access their own local variables and global variables, and cannot access the scope of the generated context called by the function constructor. (MDN)
Other words:
- You can create a function dynamically by using the new function to execute a dynamically generated functional definition JS statement.
- Functions generated through the new function, scoped globally.
- Then there are 3 kinds of communication:
把变量放到全局(扯淡)
, 函数传参
, 用call/apply把值传给函数的this
.
At first I was using call to pass the value, and now I think it's not too graceful to be passed with parameters. This is the case:
Const CONTENT = ' Console.log (data); ' ; New Function (' Data ', content); Func (// Hello World
So far, the prototype has. below to split.
Template splitting
Look at the template first:
for (var i=0; i<data.length; i++) { var item = data[i]; if (Item.age < 30) {%> <li> My name is <%=item.name%>, my age is <%=item.age%></li> <%}else{% > <li>my name is <%=item.name%>,my an age is a sercet.</li> <%}%> <%}%>
JS Logic part , by <%%>
parcel, js variable placeholder , by <%= %>
Parcel, the rest is common to stitching the HTML string part.
In other words, there are 3 parts that need to be identified with a regular:
<%%>
JS content of Logical part
<%=%>
JS content in the placeholder section
- Other
纯文本
content
The 2nd, JS placeholder part, also belongs to the stitching text. So it can be put together, that is js部分
, 拼接部分
.
Regular extraction
Of course, the choice of regular expression Ah!
Here, let's expand on the pseudo-array aspect, and how the browser's console treats the pseudo-array:
Don't pull away, just say the conclusion:
As long as there is a length property of type int , there is the Splice property of the function type . Then the browser will think of him as an array.
If the other properties are sorted by index, they can even be displayed in the console like the entries in the array.
This way of judging is called duck typing , if a thing looks like a duck, and is called like a duck, then it is duck 0_o
Back to the text, this needs to be repeated from the template, the JS logical part and the text sequentially extracted.
For each fetch, get the extracted content, this time match the last index entry (used to lift the text content). So I chose the RegExp.prototype.exec.
For example, RegExp.prototype.exec returns a collection (a pseudo-array) whose type is:
Properties/Indexes |
Description |
[0] |
All matching strings |
[1],...[n] |
Grouping captures in parentheses |
index |
The 0-based index value of the match to the character that is located in the original string |
input |
Raw string |
Through this, we can get the matching JS logic part, and through index and this match to the content, to get each JS logical part between the text content items.
Note that in global match mode, the regular expression continues to match the last matching result to the new string.
/** * Extract text from the original template/js part * * @param {string} content * @returns {Array<{type:number,txt:string}> ;} */function transform (content) {var arr = []; Returns the array that is used to save the match result var reg =/<% (?! =) ([\s\s]*?) %>/g; Regular var match for matching JS code; Match var nowindex = 0 currently matched to; The current match to the index while (match = reg.exec (content)) {////placeholder appendtxt before saving the current match (ARR, CO Ntent.substring (Nowindex, Match.Index)); Save the current match Arr.push ({type:1,//JS code txt:match[1]//Match to); Updates the current matching index Nowindex = match.index + match[0].length; }//Save text trailing Appendtxt (arr, content.substr (Nowindex)); return arr; }/** * Normal text is added to the array, the line portion is escaped * * @param {array<{type:number,txt:string}>} list * @param {string} Content */function Appendtxt (list, content) {content= Content.replace (/\r?\n/g, "\\n"); List.push ({txt:content}); }
...
Get the JS logic and text content, you can put them together, to dynamically generate a function. Note that the text content, including the JS placeholder, this place to convert a bit.
/** * template + data = "Rendered string * * @param {string} content template * @param {any} data data * @returns rendered string * /function render (content, data) { data = Data | | {}; var list = [' var tpl = '; ']; var Codearr = transform (content); Code split item Array for (var i = 0, len = codearr.length; i < Len; i++) { var item = codearr[i];//Current split // This type, or JS placeholder if (!item.type) { var txt = ' tpl+= ' + item.txt.replace (/<%= (. *?) %>/g, function (G0, G1) { return ' + ' + G1 + ' + '; }) + ' '; List.push (TXT); } else { //If it is a JS code list.push (item.txt); } } List.push (' return TPL; '); return new Function (' Data ', list.join (' \ n ')) (data); }
This completes the simple template engine, do not think the spelling string is slow.
In the modern browser (IE8 start), specifically to the operation of the string to do a lot of optimization, with + = to spell a string, than with the array push and then join the way much faster, even if put to IE7 (IE6 unclear), I test is also the spelling string fast ...
At last
Template engine this thing I searched the garden There are many, I this is fried rehash.
Making wheels is something that occasionally improves the sense of accomplishment, but it is caodan to do what you do.
Attached to GitHub address: HTTPS://GITHUB.COM/SHALLDIE/MINI-TPL
Hope that everyone money, less overtime, can write code like:D
.
Simple JS template engine