Use of template engine

template engine

The birth of template is to separate display from data. There are many template technologies, but its essence is to generate the final HTML code through the template engine. As shown in the figure below:

The template engine uses regular expressions to identify the template identifier and replaces the identifier with data. For example:
Hello, <%= name%>

The data is {name: 'wooden tree'}. After parsing through the template engine, we hope to get Hello, wooden tree. The first half of the template is an ordinary string and the second half is the template ID. we need to replace the identifier with an expression. The rendering process of the template is as follows:


The code is as follows:

function tmpl(str, obj) {
    if (typeof str === 'string') {
        return str.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
            var key = arguments[1];
            return obj[key];
        });
    }
}

var str = "Hello, <%= name%>";
var obj = {name: "Lzz"};

Engine core
The above demonstration is a simple string replacement, but for the template engine, the work to be done is more complex. The following steps are usually required:

The regular expression is used to decompose the ordinary string and template identifier. The regular expression of <% =% > is / <% = \ s * ([^% >] +) \ s *% > / g
Convert the template's expression identifier to normal language
Generate pending statements
Fill the data into the execution to generate the final string

The Demo code is as follows:

function tmpl(str, obj) {
    if (typeof str === 'string') {
        var tm = str.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
            var key = arguments[1];
            return "' + obj." + key; // Wrap normal string with 'in function string
        });

        tm = "return '" + tm; //"'Hello' + obj.name"
        var compile = new Function('obj', tm);
        return compile(obj);
    }
}

var str = "Hello, <%= name%>";
var obj = {name: "Lzz"}; // Hello, Lzz

Template logic
A slightly more powerful template engine allows you to add some logic to the template to control the final rendering of the page. For example:
var str = "<%for(var i = 0; i < 3; i++){%>name is <%= name%> <%}%>";

Here, we use <%% > to represent the logical code and <% =% > to represent the identifier to be replaced in the template. Our template code becomes as follows:

//Template logic
var tmpl = (function(){
    var cache = {};
    var strip = function(html) {
        return String(html)
        .replace(/&/g, '&amp;')//&
        .replace(/</g, '&lt;')//Left cusp
        .replace(/>/g, '&gt;')//Right cusp
        .replace(/"/g, '&quot;')//Double quotation mark“
        .replace(/'/g, '&#039;');// & apos; 'is not supported under ie
    }
    return function(str, obj){debugger;
        if (!typeof str === 'string') {
            return;
        }
        var compile = cache[str];
        if (!cache[str]) {
            //var tm = str.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
            //    var key = arguments[1];
            //    return "' + strip(" + key + ")";
            //});
            var tm = str.replace(/<%\s*([^=][^%>]*)\s*%>/g, function() {
                var key = arguments[1];
                return "';" + key + " tmp+='"; // The logic code needs to be spliced piece by piece in order to splice it into a reasonable function string and pass it to new Function
            }).replace(/<%=\s*([^%>]+)\s*%>/g, function() {
                var code = arguments[1];
                return "' + strip(" + code + ") +'"; //Wrap the code with escape. When adding template logic, pay attention to ensure that it is spliced into the correct function string
            }).replace(/<%=\s*([^%>]+)\s*%>/g, function() {
                var key = arguments[1];
                return "' + " + key + "+ '";//When adding template logic, pay attention to ensure that it is spliced into the correct function string
            });debugger;
            tm = "var tmp = \"\"; with(obj){ tmp = '" + tm + "'; } return tmp;"; //"'Hello' + obj.name"
            compile = new Function('obj', 'strip', tm);
            cache[str] = compile;
        }
        return compile(obj, strip); //In the case of precompiling, the compile function should be returned
    }
}());

var str = "<%for(var i = 0; i < 3; i++){%>name is <%= name%> <%}%>";
var obj = {name: "<script>alert(\"XSS\")</script>"};
tmpl(str, obj);

The first step is to find out the logical expression in the template. The regular expression used is
/<%\s*([=][%>])\s%>/g

str.replace(/<%\s*([^=][^%>]*)\s*%>/g, function() {
                var key = arguments[1];
                return "';" + key + " tmp+='"; // The logic code needs to be spliced piece by piece in order to splice it into a reasonable function string and pass it to new Function
            })

Note that during splicing, in order to prevent the non closure of the string in the function string from affecting the expression, we add 'before and after the key to ensure the closure of the string.
The second step is to escape the possible HTML tags

.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
                var code = arguments[1];
                return "' + strip(" + code + ") +'"; //Wrap the code with escape. When adding template logic, pay attention to ensure that it is spliced into the correct function string
            })

You also need to pay attention to the closing of the string before and after
The third step is to deal with the template identifier as before, and still pay attention to the string closure problem.

.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
                var key = arguments[1];
                return "' + " + key + "+ '";//When adding template logic, pay attention to ensure that it is spliced into the correct function string
            })

Added by szym9341 on Sat, 29 Jan 2022 23:34:17 +0200