First, what is a template engine?

The template engine takes the data and converts it into AN HTML string.

const TemplateEngine = (tpl, data) = > {
    return xxx
}

var compiled = _.template('hello <%= user %>! ');
compiled({ 'user': 'fred' });
// => 'hello fred! '
Copy the code

The LoDash template in the above example performs a similar function to the template engine.

The compiler of vue.js incorporates the functionality of the template engine.

// An example from vue.js
Vue.component('todo-item', {
  // The todo-item component now accepts one
  // "prop", similar to a custom attribute.
  // This prop is named todo.
  props: ['todo'].template: '<li>{{ todo.text }}</li>'
})
Copy the code

The

  • {{todo.text}}
  • above is the template string.

    Implementation template engine

    First, the dynamic areas of the template engine have specific rules, such as <% %> above.

    To clarify, the following text in the dynamic areas are specifically referred to <% %>.

    For this rule you can use ongoing matching.

    const tplStr = `hello <%user%>! `
    const re = [^ % > / < % (] +)? %>/g;
    Copy the code

    Re.exec (tplStr) can match user

    Here’s a description of the regular [^%>]. This is an inverted character set, indicating that %> inside brackets cannot be matched. This is exactly the dynamic region of the template string we wrote above.

    (xx)? Non-greedy match, so that there is no match to <%foo%> barzzz <%bar%>

    special-negated-character-set – JavaScript | MDN

    Dynamic regions can now be matched using re.exec, but the reality is that there is more than one dynamic region. Re.exec is therefore executed multiple times.

    const re = [^ % > / < % (] +)? %>/g;
    const tplStr = `<%foo%> barzzz <%bar%>`
    let match
    while(match = re.exec(tplStr)) {
        console.log(match)
        // <%foo%>
        // <%bar%>
    }
    Copy the code

    This will allow our template engine to be written the way it was originally written

    const TemplateEngine = (tpl, data) = > {
        const re = [^ % > / < % (] +)? %>/g;
        let match;
        while(match = re.exec(tpl)) {
            tpl = tpl.replace(match[0], data[match[1]])}return tpl;
    }
    Copy the code

    The template string can be replaced successfully as described above.

    But in our actual development process, such as vue.js template support {{todo.text}}

    So substitution is not enough. This is where the syntax runs.

    Next we need to use new Function

    Function – JavaScript | MDN

    var a = 1
    var b = 2
    var fn  = new Function('return a+b')
    fn() / / 3
    Copy the code

    This is why the vue.js template {{a + b}} supports JavaScript expressions

    Support for… In grammar,

    Suppose the template string we need to work with now looks like this

    var template = 
    'My skills:' + 
    '<%for(var index in this.skills) {%>' + 
    '<%this.skills[index]%>' +
    '< %} % >';
    Copy the code

    Replace <% and %> and concatenate them to form the following. Such a string is not a valid statement and an error is reported.

    return
    'My skills:' + 
    for(var index in this.skills) { +
    ' ' + 
    this.skills[index] +
    ' '+}Copy the code

    We need to bring for… The output of in is spliced in other forms.

    Define an array to store each line of code. We then concatenate the arrays that store the code information.

    var r = [];
    r.push('My skills:'); 
    for(var index in this.skills) {
    r.push(' ');
    r.push(this.skills[index]);
    r.push(' ');
    }
    return r.join(' ');
    Copy the code

    New Function(body) Body is the code string above

    New Function(“var r = []; return r;” )

    Var r= []; var r= []; return r;

    I left out r.paush for in}.

    So the rules for converting template strings to code strings are:

    The normal character ‘My skills:’ becomes the code r.ush (‘ XXX ‘)

    Normal dynamic region, i.e., no for… Skills [index]. Skills [index]

    For (var index in this.skills) {

    Concatenation code string

    Write the above rules in code

    let code = 'var r=[]; \\n'; const reExp = /(^( )? (for|if| | | attach) ) (. *)? /g; var add =function (line, js) {
        js ? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + '); \n') : (code += line ! =' ' ? 'r.push("' + line.replace(/"/g, '\\"'+'"); \n' : ''); return add; }Copy the code

    The js parameter of the add function is used as a marker to determine whether the region is dynamic.

    ReExp determines whether there is a dynamic region with keywords

    The full version reExp

    reExp = / (^ ()? (var|if|for|else|switch|case|break|{|}|;) ) (. *)? /g
    Copy the code

    Process the original template string

    To generate the code string, we need to define an index cursor. Used to record the position of the original template string that was matched and processed.

    let match;
    let cursor = 0;
    while(match = re.exec(html)) {
        add(html.slice(cursor, match.index))(match[1].true);
        cursor = match.index + match[0].length;
        }
    // Process the remaining unmatched template strings
    add(html.slice(cursor, html.length));
    Copy the code

    Full version of the simple template engine

    var TemplateEngine = function (html, options) {
      var re = [^ % > / < % (] +)? %>/g, reExp = / (^ ()? (if|for|else|switch|case|break|{|}))(.*)? /g,
        code = 'var r=[]; \n',
        cursor = 0, match;
      var add = function (line, js) {
        js ? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + '); \n') : (code += line ! =' ' ? 'r.push("' + line.replace(/"/g.'\ \ "') + '"); \n' : ' ');
        return add;
      }
      while (match = re.exec(html)) {
        add(html.slice(cursor, match.index))(match[1].true);
        cursor = match.index + match[0].length;
      }
      add(html.slice(cursor, html.length));
      code = (code + 'return r.join(""); ').replace(/[\r\t\n]/g.' ');
      return new Function(code).apply(options)
    }
    
    var template =
      'My skills:' +
      '<%if(this.showSkills) {%>' +
      '<%for(var index in this.skills) {%>' +
      '<%this.skills[index]%>' +
      '< %} % >' +
      '<%} else {%>' +
      'none' +
      '< %} % >';
    console.log(TemplateEngine(template, {
      skills: ["js"."html"."css"].showSkills: true
    }));
    Copy the code

    Note: Problems with escape in the reference article cause R.ush to be replaced with.push

    The above version works fine

    Refer to the link

    Lodash /lodash. Js at 4.17.15 · LoDash/loDash

    Absurd/templateengine.js at master · krasimir/ writing

    JavaScript template engine in just 20 lines