JavaScript template engine principle, several lines of code

Source: Internet
Author: User
Tags php template
JavaScript template engine principle, several lines of code 2013-12-0316: 35 byBarretLee, 765 read, 8 Comments, favorites, edit 1. preface what is a template engine, simple to say, is a string with several variables to be determined. For example: vartpl & amp; #3... syntaxHighlighter. the principle of the all JavaScript template engine, a few lines of code, by BarretLee, 765 reading, 8 Comments, favorites, edit 1. preface what is the template engine, simple to say, is a string with several variables to be determined. For example, var tpl = 'hei, my name is <% name %>, and I \'m <% age %> years old. '; use the template engine function to insert data. var data = {"name": "Barret Lee", "age": "20"}; var result = tplEngine (tpl, data); // Hei, my name is Barret Lee, and I'm 20 years old. so what is the role of this stuff? In fact, he is a preprocessor. He is familiar with Smarty when developing php, and Smarty is a php template engine, the characters to be processed in tpl are matched by data and then the corresponding html code is output. Combined with powerful cache technology, the speed and ease of use are very powerful! The same is true for JS templates. Our database stores tens of millions of data records, and each piece of data is input in the same way. For example, we cannot store thousands of "Hei, my name... ", but only save the corresponding name and age and output the results through the template. What should the JS template engine do? Let's take a look at the following code: var tpl = '<% for (var I = 0; I <this. posts. length; I ++) {'+' var post = posts [I]; %> '+' <% if (! Post. expert) {%> '+ 'Post is null' +' <%} else {%> '+' <% post. expert %> at <% post. time %> '+' <% }%> '+' <%} %> '; a basic template engine can at least ensure that the above Code can be parsed properly. For example, the input data is var data = {"posts": [{"expert": "content 1", "time": "yesterday" },{ "expert ": "content 2", "time": "today" },{ "expert": "content 3", "time": "tomorrow" },{ "expert ": "", "time": "eee"}]}; output: content 1 at yesterdaycontent 2 at todaycontent 3 at tomorrowpost is null. First stamp this demo. The following describes the principle of the template engine. II. Implementation principle of JS template engine 1. the regular expression extracts the content to be matched. For this code string, obtain the content var tpl = 'hei, my name is <% name %> through the regular expression, and I \'m <% age %> years old. '; var data = {"name": "Barret Lee", "age": "20"}; the simplest way is to use the replace function: var result = tpl. replace (/<% ([^ %>] + )? %>/G, function (s0, s1) {return data [s1] ;}); through regular expression replacement, we can easily get the result. You can try it, he officially wanted the results. But there is another problem here. Change data and tpl, var tpl = 'hei, my name is <% name %>, and I \'m <% info. age %> years old. '; var data = {"name": "Barret Lee", "info": {age ":" 20 "}; use the above method to obtain the result, no ~ Here data ["info. age"] is undefined, so we need to solve this problem in another way, that is, convert it into a real JS Code. For example, return 'hei, my name is '+ data. name + ', and I \ 'M' + data.info. age '+ 'years old. 'But then there is another problem. When the for loop and if appear in our code, the above conversion obviously does not work, for example: var tpl = 'posts: '+' <% for (var I = 0; I <post. length; I ++) {'+' <% post [I]. expert %> '+' <% }%> 'If the above method is used, return 'posts:' + for (var I = 0; I <post. length; I ++) {+ ''+ post [I]. exper + ''+} is obviously not the cause. Let's take a look at the above structure. If we can return such a result, it is quite good: 'pos Ts: 'for (var I = 0; I <post. length; I ++) {''+ post [I]. exper + ''} but we need to get a string instead of the scattered fragments above, so we can load these items into the array. 2. load array var r = []; r. push ('posts: '); r. push (for (var I = 0; I <post. length; I ++) {); r. push (''); r. push (post [I]. exper); r. push (''); r. push (}); some people will laugh when they see the above Code. The logic of the third and last lines of code is obviously incorrect. Is it so swollen? Haha, It's easy. Just do it without putting it in. var r = []; r. push ('posts: '); for (var I = 0; I <post. length; I ++) {r. push (''); r. push (post [I]. exper); r. push ('');} is a perfect logic. There are not many vulnerabilities, but how is this conversion process implemented? We must write a parsing template function. 3. Identify the js logic part var r = []; tpl. replace (/<% ([^ %>] + )? %>/G, function (s0, s1) {// finished. It seems that there is a wrong step to return to the above ridiculous logic... how can this problem be solved ?}); It seems that I have to go back to the above. The silly logic has a wrong step... what should I do? We know that JS provides us with the "class" of the constructor, var fn = new Function ("data", "var r = []; for (var I in data) {r. push (data [I]);} return r. join ('')"); fn ({"name": "barretlee", "age": "20"}); // barretlee 20 can handle this, we can link the code of the logical part and the non-logical part into a string, and then use a function similar to fn to compile the Code directly. And/<% ([^ %>] + )? %>/G, this regular expression can only match the logical part. to combine all the code, you must match the non-logical part of the code. Although the replace function is very powerful, it can also complete this task, but the implementation logic is relatively obscure, so we can handle it in another way. Let's take a look at a simple example: var reg =/<% ([^ %>] + )? %>/G; var tpl = 'hei, my name is <% name %>, and I \'m <% age %> years old. '; var match = reg.exe c (tpl); console. log (match); [0: "<% name %>", 1: name, index: 16, input: "Hei, my name is <% name %>, and I'm <% age %> years old. "length: 2] This... We wanted to get all the matching results. He actually only obtained the name and ignored the age behind it. Well, kids shoes that are a little familiar with regular expressions will surely know that they should be handled like this: var reg =/<% ([^ %>] + )? %>/G; while (match = reg.exe c (tpl) {console. log (match);} I will not elaborate on the regular expression content here. If you are interested, you can learn more about regular expressions such as match, exec, and search. Here, we mainly rely on the index attribute of match to locate the traversal position, and then use the while loop to get all the content. 4. the prototype of the engine function is similar to the following: var tplEngine = function (tpl, data) {var reg =/<% ([^ %>] + )? %>/G, code = 'var r = []; \ n', cursor = 0; // The main function is to locate the last part of the code var add = function (line) {code + = 'R. push ("'+ line. replace (/"/g, '\"') + '"); \ n' ;}; while (match = reg.exe c (tpl) {add (tpl. slice (cursor, match. index); // add non-logical parts (match [1]); // Add the logical part match [0] = "<%" + match [1] + "%>"; cursor = match. index + match [0]. length;} add (tpl. substr (cursor, tpl. length-cursor); // the last part of the code is: "years old. "code + = 'Return r. join (""); '; // return result. Here we get the code console after loading the array. log (code); return tpl ;}; in this way, test a small demo: var tpl = '<% for (var I = 0; I <this. posts. length; I ++) {'+' var post = posts [I]; %> '+' <% if (! Post. expert) {%> '+ 'Post is null' +' <%} else {%> '+' <% post. expert %> at <% post. time %> '+' <% }%> '+' <%} %> '; tplEngine (tpl, data); The returned results are satisfying: var r = []; r. push (""); r. push ("for (var I = 0; I <this. posts. length; I ++) {var post = posts [I]; "); r. push (""); r. push ("if (! Post. expert) {"); r. push ("post is null"); r. push ("} else {"); r. push (""); r. push ("post. expert "); r. push ("at"); r. push ("post. time "); r. push (""); r. push ("}"); r. push (""); r. push ("}"); r. push (""); return r. join (""); but we also need to push for, if, switch, and other things to the r array. So we have to improve the above Code, if the code containing the js logic is found in line, we should not let him in: regOut =/(^ ()? (If | for | else | switch | case | break | {| }))(.*)? /G; var add = function (line, js) {js? Code + = line. match (regOut )? Line + '\ N': 'R. push ('+ line +'); \ N': code + = 'R. push ("'+ line. replace (/"/g, '\"') + '"); \ n' ;}; so we only have to work in the last step and throw data into it! 5. It is easier to throw data into it than to complete it. By explaining the Function above, everyone should know how to do it. Return new Function (code ). apply (data); the use of apply is to bind some variable scopes in the code to data, otherwise the scope will run to global, so that the data index will have problems ~ Of course, we can optimize it again: return new Function (code. replace (/[\ r \ t \ n]/g ,'')). apply (data); match the line breaks and tab keys to make the code clean. The final code is: var tplEngine = function (tpl, data) {var reg =/<% ([^ %>] + )? %>/G, regOut =/(^ ()? (If | for | else | switch | case | break | {| }))(.*)? /G, code = 'var r = []; \ n', cursor = 0; var add = function (line, js) {js? (Code + = line. match (regOut )? Line + '\ N': 'R. push (' + line + '); \ n'): (code + = line! = ''? 'R. push ("'+ line. replace (/"/g, '\"') + '"); \ N':''); return add;} while (match = reg.exe c (tpl )) {add (tpl. slice (cursor, match. index) (match [1], true); cursor = match. index + match [0]. length;} add (tpl. substr (cursor, tpl. length-cursor); code + = 'Return r. join (""); '; return new Function (code. replace (/[\ r \ t \ n]/g ,'')). apply (data) ;}; 3. After all, the application scenario is a front-end code. Therefore, it is written to serve the front-end. Generally, we process an html template, generally, the template code is In the script tag or textarea, you must first obtain the header and then parse it. Var barretTpl = function (str, data) {// obtain the element var element = document. getElementById (str); if (element) {// textarea or input, value is taken. In other cases, innerHTML var html =/^ (textarea | input) $/I. test (element. nodeName )? Element. value: element. innerHTML; return tplEngine (html, data);} else {// is a template string, a function is generated // If the string is directly input as the template, there may be too many changes, therefore, it is easier to cache return tplEngine (str, data);} var tplEngine = function (tpl, data) {// content above, the usage is barretTpl (str, data). Here, str can be the template code or the id of a DOM element ~ Let's take a look at the two codes: https://gist.github.com/barretlee/7765698 , https://gist.github.com/barretlee/7765587 You can also directly stamp this demo. 4. There are a total of 30 or 40 lines of code for optimization and function expansion. The finished items must be a simple version, but these lines of code are enough for a simple page, if you want to optimize it, you can consider the following aspects: optimize the obtained template code, such as removing the trailing space and other escape characters. If you want to output source code similar to hehe, escape code caching is required before pushing. If a template is frequently used, you can cache it with an array in the barretTpl closure and set the delimiter by yourself.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.