Write a simple template engine

Write a simple template engine

ES5 began to support template string (Template literals), supports the following wording:

`string text ${expression} string text`;

In fact, in many template engines often have such needs, such as the commonly used doT, using similar syntax

<div>{{=1+2}}</div>
// 或者支持循环或者判断 {{for(var i in it){}}}
<span>{{=i}}</span>
{{}}}

Simple interpolation to achieve

Let's look at what a template engine to achieve the basic needs, and the cycle is not considered judgment, only supports variable operations.
Open Babel , enter

const a = 1;
console.log(`Hi\n${2 + 3}!dk${a}`);

After Babellater escaped, you can see

"use strict";

var a = 1;
console.log("Hi\n".concat(2 + 3, "!dk").concat(a));

Can be seen that Babelthe interpolated parameters extracted into concat, the calculation function parameters by self achieved Template literals. In our use, in fact, do not direct such an effect.
But counterfeit Babelpractices, we can sort out their own ideas:

  1. And put through a positive actual string interpolation disassembled
  2. By evalor new Function()implemented interpolation calculation
  3. By concat splicing, can also be used String.raw

Code is implemented as follows:

var str = "string text ${1 + 2} string text ${2 + 3} test";
function template(str) {
  var pattern = /\$\{.*?\}/g;
  var patternCapture = /\$\{(.*?)\}/g;

  // 将非插值字符串分割出来
  var strArr = str.split(pattern);

  // 将插值字符串分割出来
  var rawArr = str
    .match(patternCapture)
    .map(item => item.replace(patternCapture, "$1"));

  // eval转换
  var valueArr = rawArr.map(r => eval(r));

  // 使用reduce和concat拼接,
  return strArr.reduce(
    (acc, curr, index) => acc.concat(curr, valueArr[index] || ""),
    ""
  );
  // 或者使用String.raw
  // return String.raw({ raw: strArr }, ...valueArr);
}

console.log(template(str));

new Function

Using the above evalinterpolation has been evaluated, in fact, in normal use, evalit is not recommended. And with evalto resolve some of the judgments and conditional cycle is not very convenient.
So the next use new Function()to build a template function. Prior to this, we need to explain new Functionusage.

new Function ([arg1[, arg2[, ...argN]],] functionBody)

Is a parameter passed in front of the needed functions, the last body of the function, is a function body includes a JavaScript function definition statement string.
Second, interpolation implemented according to the above, we can use the string concatenation after the calculation of the interpolation and stitching together a normal string.
For simple interpolation, use {{}}wrap, and statements using {{~}}different. Here is a simple implementation

function render(tem, data) {
  let template = tem;
  template = template
    .replace(/[\r\n\t]/g, "")
    .replace(/\{\{~(.+?)\}\}/g, (_, p1) => {
      return '";' + p1 + ' out+="';
    })
    .replace(/\{\{(.+?)\}\}/g, (_, p1) => {
      return '"; out+=""+' + p1 + '+""; out+="';
    });
  template = 'var out=""; out += "' + template + '";return out;';
  var _render = new Function(...Object.keys(data), template);

  return _render(...Object.keys(data).map(k => data[k]));
}

var template =
  "test array{{~for (var i in group.jobs) {}}{{group.jobs[i]}}  {{~}}} test obj {{group.jobs[1]}} {{group.name}} leader是{{leader}}";

var data = {
  group: {
    name: "group1",
    jobs: ["job1", "job2"]
  },
  leader: "张三"
};

console.log(render(template, data));

When injected into the data to a template, you can use with(data){}the way, I do not like to use with, so after the parameter passed in the decomposition.

(Finish).

Guess you like

Origin www.cnblogs.com/liuyongjia/p/10962850.html
Recommended