Elementary introduction to the basic implementation of JavaScript template engine-basic knowledge

Source: Internet
Author: User

The template separates the data from the presentation, making the logic and effects of the presentation easier to maintain. Use JavaScript function objects to build a very simple template transformation engine in one step

Introduction to Templates
a template usually refers to a text embedded in a dynamic programming language code, where data and templates can be combined in some form to change different results. Templates are often used to define the form of display, which makes the data more informative and easy to maintain. For example, here is an example of a template:

<ul>
 <% for (var i in items) {%>
 <li class= ' <%= items[i].status%> ' ><%= items[i].text %></li>
 <%}%>
</ul>

If you have the following items data:

items:[
 {text: ' Text1 ', Status: ' Done '},
 {text: ' Text2 ', Status: ' Pending '},
 {text: ' Text3 ', Status: ' Pend ing '},
 {text: ' Text4 ', Status: ' Processing '}
]

In some way, the following HTML code can be generated:

<ul>
 <li class= ' done ' >text1<li>
 <li class= ' pending ' >text2<li>
 < Li class= ' pending ' >text3<li>
 <li class= ' processing ' >text4<li>
</ul>

If you do not use a template and want to achieve the same effect, the data above will show up as a result, and you need to do the following:

var temp = ' <ul> ';
for (var i in items) {
 temp + = "<li class= '" + items[i].status + "' >" + items[i].text + "</li>";
}
temp = ' </ul> ';

You can see that using templates has the following benefits:

Simplifies the writing of HTML
the ability to control the presentation of data through programming elements such as loops and conditional branching
Separation of data and presentation, making the presentation of logic and effect easier to maintain
Template engine
by analyzing templates, the program that combines data and templates to output the final results is called the template engine, there are many types of templates, and there are many types of template engines to correspond to. An older template, called Erb, is used in many web frameworks, such as: asp.net, Rails ... The example above is the example of Erb. Two core concepts in Erb: Evaluate and interpolate. On the surface evaluate refers to the part contained in the <%%>, Interpolate is the part contained in the <%=%>. From the point of view of the template engine, the parts in the evaluate are not directly exported to the results, generally used for process control, and the parts in the interpolate are directly exported to the results.

From the implementation of the template engine, it is necessary to rely on the dynamic compilation of the programming language or the characteristics of dynamic interpretation to simplify implementation and improve performance. For example, ASP.net uses. NET's dynamic compilation to compile the template into a dynamic class and dynamically execute the code in the class with reflection. This implementation is actually quite complex, because C # is a static programming language, but using JavaScript can use function to implement a simple template engine with very little code. This article will implement a simple Erb template engine to show the power of JavaScript.

Template text Transformation
for the above example, review the difference between using a template and not using a template:

Template wording:

<ul>
 <% for (var i in items) {%>
 <li class= ' <%= items[i].status%> ' ><%= items[i].text %></li>
 <%}%>
</ul>

Non-template formulation:

var temp = ' <ul> ';
for (var i in items) {
 temp + = "<li class= '" + items[i].status + "' >" + items[i].text + "</li>";
}
temp = ' </ul> ';

Careful observation, in fact, the two methods are very "similar", can find a sense of one by one correspondence. If you can change the template's text into code execution, you can implement template transformation. There are two principles in the process of transformation:

Encounter plain text directly as string concatenation
Encounter interpolate (ie <%=%>), and use the contents as a variable to stitch in the string
Encounter evaluate (ie <%%>) directly as code
The above example is transformed according to the above principle, and then a total function is added:

var template = function (items) {
 var temp = ';
 Start Transform
 temp = ' <ul> ';
 for (var i in items) {
 temp + = "<li class= '" + items[i].status + "' >" + items[i].text + "</li>";
 }
 Temp + = ' </ul> ';
}

Finally, execute this function and pass in the data parameter:

var result = template (items);

JavaScript dynamic functions
The transformation logic above is actually very simple, but the key problem is that the template is variable, which means that the generated program code must also be generated and executed at runtime. Fortunately, JavaScript has many dynamic features, one of which is a powerful feature. We usually use the function keyword to declare functions in JS, and we rarely use function. In JS, function is literal syntax, JS runtime will convert literal function to function object, so actually function provides a more low-level and flexible mechanism.

The syntax for creating functions directly with function classes is as follows:

var function_name = new Function (arg1, arg2, ..., argn, function_body)

For example:

Create dynamic function 
var sayhi = new Function ("Sname", "smessage", "Alert" (\ "Hello \" + sname + smessage); ");
Execute 
sayhi (' Hello ', ' world ');

function bodies and parameters can be created by strings! So cool! With this feature, you can convert the template text into a string of function bodies so that you can create dynamic functions to invoke dynamically.

Realize the idea
first, the regular formula is used to describe interpolate and evaluate, and parentheses are used to group capture:

var interpolate_reg =/<%= ([\s\s]+?) %>/g;
var evaluate_reg =/<% ([\s\s]+?) %>/g;

To combine the two regular patterns in a continuous match of the entire template, note that all strings that match interpolate can match evaluate, so interpolate needs to have a higher priority:

var matcher =/<%= ([\s\s]+?) %>|<% ([\s\s]+?) %>/g

Design a function to transform templates, input parameters to template text strings and data Objects

var matcher =/<%= ([\s\s]+?) %>|<% ([\s\s]+?) %>/g
//text: Incoming template text string
//data: Data Object
var template = function (Text,data) {...}

With the Replace method, regular matches and "replacements" are not actually intended to replace interpolate or evaluate, but rather to build a "method body" in the matching process:

var matcher =/<%= ([\s\s]+?) %>|<% ([\s\s]+?) %>/g
//text: Incoming template text string
//data: Data Object
var template = function (text,data) {
 var index = 0;//Record where is the current scan? C5/>var function_body = "var temp = ';";
 Function_body + = "temp + +";
 Text.replace (Matcher,function (match,interpolate,evaluate,offset) {
 //When the first match is found, an expression
 that stitching the previous part as an ordinary string Function_body + + text.slice (index,offset);
 
 If it is <% ...%> directly as a code fragment, evaluate is the captured grouping
 if (evaluate) {function_body + + "
  ;" + Evaluate + "temp + +"; 
   }
 //If it is <%= ...%> concatenation string, interpolate is the captured grouping
 if (interpolate) {
  function_body = "' +" + Interpolate + "+ '";
 }
 Increment index, skip evaluate or interpolate
 index = offset + match.length;
 The return here is meaningless because the key is not to replace text, but to build function_body return
 match;
 The final code should be to return temp
 function_body = "'; returns temp;";
}

At this point, although Function_body is a string, but the content is actually a function of code, you can use this variable to dynamically create a function object, and through the data parameter call:

var render = new Function (' obj ', function_body);
return render (data);

So render is a way to invoke that the code inside the method is constructed from the contents of the template, but the approximate framework should be this:

function render (obj) {
 var temp = ';
 Temp = = ...
 ...
 return temp;
}

Note that the parameter of the method is obj, so the variable referenced inside the template should be obj:

<script id= ' template ' type= ' javascript/template ' >
 <ul>
 <% for (var i in obj) {%>
  < Li class= "<%= obj[i].status%>" ><%= obj[i].text%></li> <%
 }%>
 </ul>
</script>

It's OK to appear here, but there is a problem that must be solved. The template text may contain \ r \ n \u2028 \u2029 characters, which can be faulted if they appear in the code, such as the following code is wrong:

temp = '
 <ul>
 ' + ...;

What we want to see is this code:

temp + = ' \ \t\t<ul>\n ' + ...;

This requires that \ nthe previous \ escaped into \ Can eventually become a literal \\n.

In addition, there is a problem, the above code can not be the last evaluate or interpolate the next part of the stitching in, the solution to this problem is very simple, just add a line at the end of the match can be:

var matcher =/<%= ([\s\s]+?) %>|<% ([\s\s]+?) %>|$/g;

Relatively complete code

var matcher =/<%= ([\s\s]+?) %>|<% ([\s\s]+?) Special characters in%>|$/g//template text escape processing var escaper =/\\| ' |
\r|\n|\t|\u2028|\u2029/g; var escapes = {"": "'", ' \ \ ': ' \ \ \ \ r ': ' r ', ' \ n ': ' n ', ' \ t ': ' t ', ' \u2028 ': ' u2028 ', ' \u2029 '

: ' u2029 '}; Text: Incoming template text string//data: Data object var template = function (text,data) {var index = 0;//Record where is the current scan? var function_body = "var t
 EMP = '; ';
 Function_body + = "temp + +";
 Text.replace (Match,interpolate,evaluate,offset) {//after finding the first match, adds the handle escape character to the expression//that the previous section is spliced as a normal string matcher,function

 Function_body + = Text.slice (index,offset). Replace (Escaper, function (match) {return ' \ ' + Escapes[match];});
 If it is <% ...%> directly as a code fragment, evaluate is the captured grouping if (evaluate) {function_body + + ";" + Evaluate + "temp + +";
 //If it is <%= ...%> concatenation string, interpolate is the captured grouping if (interpolate) {function_body + + "+" + interpolate + "+";
 }//Incrementing index, skipping evaluate or interpolate index = offset + match.length; The return here has no meaning, because the key is not replacing text, but building function_body return match;
 });
 The final code should be to return temp function_body = "'; returns temp;";
 var render = new Function (' obj ', function_body);
return render (data);

 }

The calling code can be like this:

<script id= ' template ' type= ' javascript/template ' >
 <ul>
 <% for (var i in obj) {%>
  <li class= "<%= obj[i].status%>" ><%= obj[i].text%></li> <%
 }%>
 </ul>
< /script> ...

var text = document.getElementById (' template '). InnerHTML;
var items = [
 {text: ' Text1 ', Status: ' Done '},
 {text: ' Text2 ', Status: ' Pending '},
 {text: ' Text3 ', statu S: ' Pending '},
 {text: ' Text4 ', Status: ' Processing '}
];
Console.log (Template (Text,items));

As we can see, we have implemented a simple template with very little code.

The legacy of the problem
There are a few more details to be noted:

    • Because <% or%> are the boundary characters of the template, if the template needs to output <% or%>, then you need to design an escape method
    • If the data object contains null, it is obvious that you do not want the final output ' null ', so you need to consider NULL in Function_body code
    • It may not be convenient to use the parameter reference data for obj every time in the template, and you can add with Function_body (obj| | {}) {...}, so that the properties of obj can be used directly in the template
    • Can be designed to return the render, rather than return the results of the transformation, so that the external can cache generated functions to improve performance
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.