原理就是正则替换 + 字符串拼接,以 ejs 为例,具体步骤为:
- 读取模板文件得到原始字符串
- 正则替换
<% %>
和 <%= %>
- 拼接成代码形式的字符串
- 使用
new Function
将字符串转化成函数
- 函数内部使用
with
进行取值
效果预览
不妨写一个 index.html,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!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=>{%><li><%=item%></li><%})%> </ul> <div> 姓名:<%=obj.name%>,年龄:<%=obj.age%> </div> </body> </html>
|
先用原生的 ejs 测试一下输出效果:
1 2 3 4 5 6 7 8 9 10 11
| const fs = require('fs') const ejs = require('ejs') const data = { title: 'EJS引擎', date: new Date('2020-02-02'), obj: { name: '张三', 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)
|
可以看到输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>EJS</title> </head> <body> <h2>EJS引擎</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> 姓名:张三,年龄:10 </div> </body> </html>
|
如何实现
整个模板渲染其实就是一个大的字符串,其中穿插着 EJS 语法,主要是 <%=xx %>
和 <% xx %>
语法:
处理 <%=xx %>
对于 <%=xx %>
这种语法,我们很容易想到直接用 data 里面的值替换进去即可,正则为:
1
| tpl = tpl.replace(/<%=(.+?)%>/g, '${$1}')
|
替换之后的字符串为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!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=>{%><li>${item}</li><%})%> </ul> <div> 姓名:${obj.name},年龄:${obj.age} </div> </body> </html>
|
处理 <% xx %>
对于 <% xx %>
这种语法,因为里面有 JS 语句,要想让其执行并得到返回结果则必须让其运行起来,可以拼接出一个完整的可运行的 JS 代码:
1 2 3 4
| 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
|
得到的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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> 姓名:${obj.name},年龄:${obj.age} </div> </body> </html> `} return str
|
然后将其包装在一个 function 里面并运行即可:
1 2
| const f = new Function('data', tpl) f(data)
|
输出结果跟官方一致。这种拼代码字符串的方式,任何 JS 语句都可以渲染出来,例如下面的 if 语句:
1 2 3
| <% if (obj) { %> <h2><%= obj.name %></h2> <% } %>
|