The principle is regular replacement + string concatenation, taking EJS as an example, the specific steps are as follows:

  1. Read the template file for the original string
  2. Regular replacementThe < % % >< % = % >
  3. Concatenate a string into code
  4. usenew FunctionConvert strings into functions
  5. Internal use of functionswithvalues

Results the preview

Instead write an index.html that says something like this:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>EJS</title>
  </head>
  <body>
    <h2><%=title %></h2>
    <p><%=date%></p>
    <ul>
      <%arr.forEach(item=>{%><li><%=item%></li>The < %}) % ></ul>
    <div>Name: <%=obj.name%>, age: <%=obj.age%></div>
  </body>
</html>
Copy the code

Let’s test the output with native EJS:

const fs = require('fs')
const ejs = require('ejs')
const data = {
  title: 'EJS engines'.date: new Date('2020-02-02'),
  obj: { name: 'Joe'.age: 10 },
  arr: [1.2.3.4.5],}const tpl = fs.readFileSync('index.html'.'utf-8')
const html = ejs.render(tpl, data)
console.log(html)
Copy the code

You can see that the output is:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>EJS</title>
  </head>
  <body>
    <h2>EJS engine</h2>
    <p>Sun Feb 02 2020 08:00:00 GMT+0800 (GMT+08:00)</p>
    <ul>
      <li>1</li><li>2</li><li>3</li><li>4</li><li>5</li>
    </ul>
    <div>Name: Zhang SAN, age: 10</div>
  </body>
</html>
Copy the code

How to implement

The entire template rendering is just a large string interspersed with EJS syntax, mainly <%=xx %> and <% xx %> syntax:

To deal with<%=xx %>

<%=xx %> <%=xx %> <%=xx %>

tpl = tpl.replace(/ < % = (. +?) %>/g.'${$1}')
Copy the code

The replacement string is:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>EJS</title>
  </head>
  <body>
    <h2>${title }</h2>
    <p>${date}</p>
    <ul>
      <%arr.forEach(item=>{%><li>${item}</li>The < %}) % ></ul>
    <div>Name: ${obj.name}, age: ${obj.age}</div>
  </body>
</html>
Copy the code

To deal with<% xx %>

For the <% xx %> syntax, because it contains JS statements, it must be run in order to execute and return the result. You can concatenate a complete runnable JS code:

const head = 'let str = ``\r\nwith(data){\r\nstr+=`'
const body = tpl.replace(/ < % (. +?) %>/g.'`\r\n$1\r\nstr+=`')
const tail = '`}\r\nreturn str'
tpl = head + body + tail
Copy the code

The resulting code looks like this:

let str = ` `
with(data){
str+=` <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>EJS</title> </head> <body> <h2>${title }</h2>
    <p>${date}</p>
    <ul>
      `
arr.forEach(item= >{
str+=`<li>${item}</li>`
})
str+=</ul> <div> Name:${obj.name}Age:${obj.age}
    </div>
  </body>
</html>
`}
return str
Copy the code

Then wrap it in a function and run it:

const f = new Function('data', tpl)
f(data)
Copy the code

The output is consistent with the official results. This way, any JS statement can be rendered, such as the following if statement:

<% if (obj) { %>
  <h2><%= obj.name %></h2>The < %} % >Copy the code