Exquisite tmpl
There are many open-source front-end templates, but the "javascript micro templating" developed by John Resig, jQuery's author, is the most subtle. With just a few clicks, the core functions of the template engine are implemented.
See the author's blog: http://ejohn.org/blog/javascript-micro-templating/ for its introduction and usage
Let's first look at his source code:
Copy codeThe Code is as follows: (function (){
Var cache = {};
This. tmpl = function (str, data ){
Var fn =! /\ W/. test (str )?
Cache [str] = cache [str] |
Tmpl (document. getElementById (str). innerHTML ):
New Function ("obj ",
"Var p = [], print = function () {p. push. apply (p, arguments) ;};" +
"With (obj) {p. push ('" +
Str
. Replace (/[\ r \ t \ n]/g ,"")
. Split ("<%"). join ("\ t ")
. Replace (/(^ | %>) [^ \ t] *) '/g, "$1 \ r ")
. Replace (/\ t = (.*?) %>/G, "', $1 ,'")
. Split ("\ t"). join ("');")
. Split ("%>"). join ("p. push ('")
. Split ("\ r"). join ("\\'")
+ "');} Return p. join ('');");
Return data? Fn (data): fn;
};
})();
Despite being small and dirty, the sparrow has a caching mechanism and logic support in addition to basic data appending. Now, if I want to rank the most energy-efficient custom function in javascript, the first place is the $ function (document. getElementById Lite version), and the second place is tmpl.
Of course, it is not perfect. I found some problems during use:
Tmpl weaknesses
1. escape characters cannot be correctly processed, for example:Copy codeThe Code is as follows: tmpl ('<% = name %> // <% = id %>', {name: 'sugar bread ', id: '20160901 '});
It will report an error. If it works properly, it should output: sugar cake/1987
In fact, it is easy to solve. Add a regular line to escape the escape character:Copy codeThe Code is as follows: str. replace (// \/g ,"\\\\")
2. Sometimes it cannot correctly distinguish whether the first parameter is ID or template.
If the Page Template ID is underlined, for example, tmpl-photo-thumb, it does not search for the template with this name, and it is considered that the input is the original template and the output is compiled directly.
The most intuitive difference between the original template and element id is whether it contains spaces. Therefore, modify the regular expression:
View sourceprint? 1! /\ S/. test (str)
3. It also contains a code for testing and can be deleted.Copy codeThe Code is as follows: print = function () {p. push. apply (p, arguments );}
Tmpl efficiency concerns
Until I read a soft article about YayaTemplate from Baidu mux some time ago, the author of the original article tested the efficiency of various popular template engines, and finally concluded that YayaTemplate is the fastest. Although the test result tmpl is inferior to YayaTemplate, it also dispelled my concerns about performance. In practice, it is similar to the traditional String concatenation. They only have to perform super-large-scale parsing to have a large performance gap. (Ultra-large scale? Javascript itself is not suitable for this. If the programmer inserts thousands of List data records into the browser at a time and it is extremely slow, there is no doubt: the problem lies in this programmer, and he will not cherish the user's browser .)
When talking about the engine efficiency ranking, I don't think this is not the top standard for measuring the template engine. The template syntax is also an important part. At this time, the template syntax of YayaTemplate is much obscure, it is clever in template syntax to save a few regular expressions.
First, the source code of YayaTemplate is displayed:Copy codeThe Code is as follows: // author: yaya, jihu
// Uloveit.com.cn/template
// How to use? YayaTemplate ("xxx"). render ({});
Var YayaTemplate = YayaTemplate | function (str ){
// Core Analysis Method
Var _ analyze = function (text ){
Return text. replace (/{\$ (\ s | \ S )*? \ $}/G, function (s ){
Return s. replace (/("| \)/g," \ $1 ")
. Replace ("{$", '_ s. push ("')
. Replace ("$ }",'");')
. Replace (/{\ % ([\ s \ S] *?) \ %}/G, '", $1 ,"')
}). Replace (/\ r | \ n/g ,"");
};
// Intermediate code
Var _ temp = _ analyze (document. getElementById (str )? Document. getElementById (str). innerHTML: str );
// Return generator render Method
Return {
Render: function (mapping ){
Var _ a = [], _ v = [], I;
For (I in mapping ){
_ A. push (I );
_ V. push (mapping [I]);
}
Return (new Function (_ a, "var _ s = [];" + _ temp + "return _ s ;")). apply (null, _ v ). join ("");
}
}
};
Why is tmpl slower than YayaTemplate if we try to solve the performance problem to a high degree of "academic problem?
Syntax Parsing? Although YayaTemplate uses a novel javascript wrapped html method as the template syntax, it eventually needs to be parsed into a standard javascript syntax using a regular expression. Here, the regular expression efficiency will not be much different, both parties use the cache mechanism to ensure that only the original template is parsed once.
Data conversion? The template engine will save the data in the form of variables in the closure to get the template. Here we will first compare the variable declaration mechanism of both parties:
YayaTemplate is implemented in the form of traditional parameter passing. It traverses the data object, separates the Object Name and value, and uses the object member name as the new Function parameter name (that is, the variable name) respectively ), then, use the appley call method of the function to pass those parameters.
Tmpl is implemented using a with statement that is not commonly used in javascript. The implementation method is concise, saving the keyword var.
The performance problem of tmpl lies in. The with statement provided by javascript is intended to access the attributes of objects more quickly. Unfortunately, the existence of the with statement in the language seriously affects the speed of the javascript engine, because it blocks the lexical scope binding of variable names.
Optimize tmpl
If tmpl is removed from the with statement, the performance of passing parameters using the traditional method is greatly improved. After measurement, firefox can be increased by 5 times, chrome 0.24 million times, and opera 2.4 times, safari 2.1 times, IE6 1.1 times, IE9 1.35 times, And YayaTemplate.
Test address: http://www.planeart.cn/demo/tmpl/tmpl.html
Final code of tmpl optimized version:Copy codeThe Code is as follows :/**
* Micro template engine tmpl 0.2
*
* 0.2 update:
* 1. Fixed the BUG of Escape Character and id judgment.
* 2. Discard inefficient with statements to increase the execution efficiency by up to 3.5 times.
* 3. Use random internal variables to prevent conflicts with template Variables
*
* @ Author John Resig, Tang Bin
* @ See http://ejohn.org/blog/javascript-micro-templating/
* @ Name tmpl
* @ Param {String} The template content or the element ID containing the template content
* @ Param {Object} additional data
* @ Return {String} the parsed Template
*
* @ Example
* Method 1: embed a template on the page
* <Script type = "text/tmpl" id = "tmpl-demo">
* <Ol title = "<% = name %>">
* <% For (var I = 0, l = list. length; I <length; I ++) {%>
* <Li> <% = list [I] %> </li>
* <%}%>
* </Ol>
* </Script>
* Tmpl ('tmpl-demo', {name: 'demo data', list: [202, 96,133,134]})
*
* Method 2: directly import the template:
* Var demoTmpl =
* '<Ol title = "<% = name %>">'
* + '<% For (var I = 0, l = list. length; I <length; I ++) {%>'
* + '<Li> <% = list [I] %> </li>'
* + '<%}%>'
* + '</Ol> ';
* Var render = tmpl (demoTmpl );
* Render ({name: 'demo data', list: [202, 96,133,134]});
*
* The difference between the two methods is that the first one automatically caches the compiled template,
* The second type of cache is handed over to external object control, for example, the render variable in example 2.
*/
Var tmpl = (function (cache, $ ){
Return function (str, data ){
Var fn =! /\ S/. test (str)
? Cache [str] = cache [str]
| Tmpl (document. getElementById (str). innerHTML)
: Function (data ){
Var I, variable = [$], value = [[];
For (I in data ){
Variable. push (I );
Value. push (data [I]);
};
Return (new Function (variable, fn. $ ))
. Apply (data, value). join ("");
};
Fn. $ = fn. $ | $ + ". push ('"
+ Str. replace (// \\/ g ,"\\\\")
. Replace (/[\ r \ t \ n]/g ,"")
. Split ("<%"). join ("\ t ")
. Replace (/(^ | %>) [^ \ t] *) '/g, "$1 \ r ")
. Replace (/\ t = (.*?) %>/G, "', $1 ,'")
. Split ("\ t"). join ("');")
. Split ("%>"). join ($ + ". push ('")
. Split ("\ r"). join ("\\'")
+ "'); Return" + $;
Return data? Fn (data): fn;
}) ({}, '$' + (+ New Date ));
The template engine depends on the Function constructor. Like eval, it provides methods to access the javascript parsing engine using text, which also significantly reduces the performance, but there is no other way in javascript.
The Function constructor also limits the parameter names. Therefore, the data member names must be consistent with the javascript variable names. Otherwise, an error is returned. Fortunately, this error can be immediately detected during operation without being a mine.
Tips for using tmpl
I. Cache Optimization.
By default, tmpl caches and optimizes the templates embedded on the page (when the first parameter is ID). It only analyzes the template once. If the original template is directly passed into the first parameter of tmpl and needs to be used multiple times, it is recommended to cache it with public variables and use it when parsing data, to achieve the same optimization effect. For example:Copy codeThe Code is as follows: // generate template Cache
Var render = tmpl (listTmpl );
// The template can be called multiple times
Elem. innerHTML = render (data1 );
Elem. innerHTML = render (data2 );
...
2. Avoid system crash caused by undefined variables.
If a variable output is defined in the template and the input data is missing, an undefined variable error will occur, causing the entire program to crash. If data integrity cannot be ensured, there is still a way to test its members. In the original version, it implies that the variable stores the original input data, that is, obj. In my upgraded version, it is the keyword "this", for example:Copy codeThe Code is as follows: <% if (this. dataName! = Undefined) {%>
<% = DataName %>
<% }%>
3. debug the template.
Because the template engine is a javascript Engine called by text, the debugging tool cannot locate the wrong line. In the upgraded version, you can use the debugging tool to output the compiled template cache. For example, to debug this template:Copy codeThe Code is as follows: <script id = "tmpl" type = "text/tmpl">
<Ul>
<% For (var I = 0, l = list. length; I <l; I ++) {%>
<Li> <% = list [I]. index %>. user: <% = list [I]. user %>; website: <% = list [I]. site %> </li>
<% }%>
</Ul>
Output cache:Copy codeThe Code is as follows: window. console (tmpl ('tmpl'). $ );
Log result:Copy codeThe Code is as follows: "$1318348744541. push ('
<Ul> '); for (var I = 0, l = list. length; I <l; I ++) {$1318348744541. push ('
<Li> ', list [I]. index ,'. user: ', list [I]. user, '; website:', list [I]. site, '</li>
');} $1318348744541. push (' </ul>
'); Return $1318348744541"
Now you can see the javascript statements compiled by the template engine. Check whether the template has any errors. ($1318348744541 is a temporary array with a random name, which can be ignored)
Finally, I am very grateful to the original tmpl author and the YayaTemplate author for their contributions. For this reason, I have the opportunity to analyze the implementation mechanism in depth to solve the problem and benefit from it. Share it with others.
Tang bin-2011.10.09-Hunan-Changsha