This article mainly introduces the basic implementation method of the JavaScript template engine and explains how to use function objects to write simple templates step by step. If you need a template, you can refer to the template below to separate data and presentation, makes the presentation logic and effect easier to maintain. Build an extremely simple template conversion engine step by step using javascript Function objects
Template Introduction
A template usually refers to the text embedded with some dynamic programming language code. The combination of data and templates can change different results. Templates are usually used to define the display form, which makes data presentation richer and easier to maintain. For example, the following is an example of a template:
<% for(var i in items){ %>
- <%= items[i].text %>
<% } %>
If the following items data exists:
items:[ { text: 'text1' ,status:'done' }, { text: 'text2' ,status:'pending' }, { text: 'text3' ,status:'pending' }, { text: 'text4' ,status:'processing' }]
In some way, the following Html code can be generated:
If you do not use a template and want to achieve the same effect, to display the above data as the result, you need to do the following:
var temp = '
';for(var i in items){ temp += "
- " + items[i].text + "
";}temp += '
';
It can be seen that using templates has the following advantages:
Simplified html writing
Through programming elements (such as loops and condition branches), data presentation is more controllable
Data and presentation are separated to make the presentation logic and effect easier to maintain.
Template engine
By analyzing templates, the program that combines data and templates to output the final result is called the template engine. There are many templates, and there are also many corresponding template engines. An old template called ERB is used in many web frameworks, such as ASP. NET and Rails... The above example is the example of ERB. Two core concepts in ERB: evaluate and interpolate. On the surface, evaluate refers to the part contained in <%>, and interpolate refers to the part included in <% = %>. From the perspective of the template engine, the part in evaluate is not directly output to the result, which is generally used for process control. The part in interpolate is directly output to the result.
The implementation of the template engine depends on the dynamic compilation or Interpretation Features of the programming language to simplify implementation and improve performance. For example, ASP. NET uses the dynamic compilation of. NET to compile the Template into a dynamic class and reflect the code in the dynamic execution class. This implementation is actually complicated, because C # Is a static programming language, but javascript can use functions to implement a simple template engine with very little code. This article implements a simple ERB template engine to show the strength of javascript.
Template text Conversion
For the above example, review the differences between using a template and not using a template:
Template syntax:
<% for(var i in items){ %>
- <%= items[i].text %>
<% } %>
Non-template syntax:
var temp = '
';for(var i in items){ temp += "
- " + items[i].text + "
";}temp += '
';
Observe carefully. In fact, these two methods are very similar and can find a one-to-one correspondence in a certain sense. If you can convert the template text into code execution, you can implement template conversion. There are two principles in the conversion process:
Directly concatenate common text into strings
Interpolate (that is, <% = %>) is used to concatenate the content into a string as a variable.
If you encounter evaluate (that is, <%>), directly use it as the code
Convert the above example according to the above principles and add a total function:
Var template = function (items) {var temp = ''; // start to convert temp + ='
'; For (var I in items) {temp + ="
- "+ Items [I]. text +"
";} Temp + ='
';}
Finally, execute this function and pass in the data parameters:
var result = template(items);
Javascript dynamic Functions
It can be seen that the above conversion logic is actually very simple, but the key issue is that the template is changed, 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 Function. We usually use the function keyword to declare a Function in js, and rarely use a function. In JavaScript, function is a literal syntax. When Javascript is run, the literal function is converted into a Function object. In fact, Function provides a more underlying and flexible mechanism.
The syntax for directly creating a Function using the Function class is as follows:
var function_name = new Function(arg1, arg2, ..., argN, function_body)
For example:
// Create a dynamic Function var sayHi = new Function ("sName", "sMessage", "alert (\" Hello \ "+ sName + sMessage );"); // execute sayHi ('hello', 'World ');
Function bodies and parameters can be created using strings! So cool! With this feature, you can convert the template text to the string of the function body, so that you can create dynamic functions for dynamic calls.
Implementation
First, use the regular expression to describe interpolate and evaluate. Parentheses are used for grouping and capturing:
var interpolate_reg = /<%=([\s\S]+?)%>/g;var evaluate_reg = /<%([\s\S]+?)%>/g;
These two regular expressions are merged for continuous matching of the entire template. However, note that all strings matching interpolate can match evaluate, so interpolate requires a higher priority:
var matcher = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g
Design a function to convert the template. The input parameter is the template text string and data object.
Var matcher =/<% = ([\ s \ S] + ?) %> | <% ([\ S \ S] + ?) %>/G // text: input template text string // data: data Object var template = function (text, data ){...}
Use the replace method to perform regular expression matching and "Replacement". In fact, our goal is not to replace interpolate or evaluate, but to build a "method body" during the matching process ":
Var matcher =/<% = ([\ s \ S] + ?) %> | <% ([\ S \ S] + ?) %>/G // text: input template text string // data: data Object var template = function (text, data) {var index = 0; // record where var function_body = "var temp =''; "; function_body + =" temp + = '"; text. replace (matcher, function (match, interpolate, evaluate, offset) {// after finding the first match, use the previous part as the regular String concatenation expression function_body + = text. slice (index, offset); // if it is <%... %> directly serves as a code snippet. evaluate is the captured group if (evaluate) {function_body + = "';" + evaluate + "temp + = '";} // if it is <% =... %> concatenate a string. interpolate is the captured group if (interpolate) {function_body + = "'+" + interpolate + "+'";} // increments the 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 + = "'; return temp ;";}
At this point, function_body is a string, but the content is actually a piece of function code. You can use this variable to dynamically create a function object and call it through the data parameter:
var render = new Function('obj', function_body);return render(data);
In this way, render is a method that can be called. The code inside the method is constructed by the template content, but the general framework should be like this:
function render(obj){ var temp = ''; temp += ... ... return temp;}
Note that the method parameter is obj, so the variable referenced in the template should be obj: