开始手写之前,我们先看看模板引擎应该是什么样的,在用koa开发后台服务的时候,我们用过ejs模板引擎,其作用是把模板渲染成html代码。下面是一个具体的使用例子。
npm install ejs
在koa中使用ejs模板引擎。
index.js
const Koa = require('koa')
const views = require('koa-views')
const path = require('path')
const app = new Koa()
// 加载模板引擎
app.use(views(path.join(__dirname, './view'), {
extension: 'ejs'
}))
app.use( async ( ctx ) => {
let title = 'hello koa2'
await ctx.render('index', {
title,
})
})
app.listen(3000)
view/index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<p>EJS Welcome to <%= title %></p>
</body>
</html>
除了ejs,还有很多其他的模板引擎。
那这些模板引擎具体是怎么实现的呢?
下面我们来手写一个简单的类ejs模板引擎。
实现模板引擎先要定义模板的语法,这里我们就重新不定义了,直接使用ejs的语法。
我们只实现最简单的几个语法:
<%
'脚本' 标签,用于流程控制,无输出。<%=
输出数据到模板(输出是转义 HTML 标签)%>
一般结束标签先贴一下待编译的模板。
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
思路是把模板转换为JavaScript代码并执行,输出转码后的html代码。
观察一下上面的代码,我们可以先推出来,转换后的JavaScript代码。
echo(`<ul>`); //echo用于打印出html代码
for(var i=0; i<data.supplies.length; i++) {
echo(`<li>`);
echo( data.supplies[i] );
echo(`</li>`);
}
echo(`</ul>`);
下面是echo的实现。
var output = "";
function echo(html){
output += html;
}
我们最后生成的,用于输出html的JavaScript代码是下面这样的。
(function parse(data){
// stores the parsed output
var output = "";
// appends HTML to the parsed template
function echo(html){
output += html;
}
// contains echos, etc
echo(`<ul>`);
for(var i=0; i<data.supplies.length; i++) {
echo(`<li>`);
echo( data.supplies[i] );
echo(`</li>`);
}
echo(`</ul>`);
return output;
})
下面列一下对不同字符串的处理方式。
<%
%>
里面的字符,保留为js逻辑<%=
%>
里面的字符,保留js逻辑,且其值输出为html代码。对这些处理方式,着手实现。
正则/<%=(.+?)%>/g
匹配第三种类型的字符串,替换为\n echo( $1 ); \n
。
正则/<%([\s\S]+?)%>/g
匹配第二种类型的字符串,替换为\n $1 \n
。
其他不是插值的字符,直接\n echo( $1 ); \n
,由于正则取反比较复杂,这里巧妙转换一下写法,改为在开头和每个类型二三的结尾加一个 "echo(`",结尾加一个结束符号,也能达到统一的结果。
最终完整的代码如下:
function compile(template){
const evalExpr = /<%=(.+?)%>/g;
const expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
let div = document.getElementById("app");
let parse = eval(compile(template));
console.log(parse)
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });