模板引擎的实现原理
模板引擎是通过字符串拼接得到的
let template = 'hello <% name %>!' let template = 'hello ' + name + '!'
字符串是通过 new Function
执行的
let name = 'world' let template = ` let str = 'hello ' + name + '!' return str ` let fn = new Function('name', template) console.log(fn(name)) // hello world!
将模板转换为字符串并通过函数执行返回
let template = 'hello <% name %>!' let name = 'world' function compile (template) { let html = template.replace(/<%([\s\S]+?)%>/g, (match, code) => { return `' + ${code} + '` }) html = `let str = '${html}'; return str` return new Function('name', html) } let str = compile(template) console.log(str(name)) // hello world!
函数只能接收一个 name
变量作为参数,功能太单一了,一般会通过对象来传参,with
来减少变量访问。
with 功能
let params = { name: '张三', age: 18 } let str = '' with (params) { str = `用户${name}的年龄是${age}岁` } console.log(str) // 用户张三的年龄是18岁
实现简单的模板引擎
let template = 'hello <% name %>!' let name = 'world' function compile (template) { let html = template.replace(/<%([\s\S]+?)%>/g, (match, code) => { return `' + ${code.trim()} + '` }) html = `'${html}'` html = `let str = ''; with (params) { str = ${html}; } return str` return new Function('params', html) } let str = compile(template) console.log(str({ name })) // hello world!
完善部分功能
let str = `<ul> <% users.forEach(function(user){ %> <li><%= user %></li> <% }) %> </ul>`; let render = function (complied, data) { // 转译HTML let escape = (html) => { return String(html) .replace(/&(?!\w+;)/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; return complied(data, escape); } // 子模板include导入 var VIEW_FOLDER = './views'; var files = {}; var preComplie = function (str) { var replaced = str.replace(/<%\s+(include.*)\s+%>/g, function (match, code) { var partial = code.split(/\s/)[1]; if (!files[partial]) { files[partial] = fs.readFileSync(path.join(VIEW_FOLDER, partial), 'utf8'); } return files[partial]; }); // 多层嵌套,继续替换 if (str.match(/<%\s+(include.*)\s+%>/)) { return preComplie(replaced); } else { return replaced; } }; function complie (str) { str = preComplie(str); let tpl = str.replace(/<%=([\s\S]+?)%>/g, (match, code) => { // 需要转义 return `' + escape(${code.trim()}) + '` }).replace(/<%-([\s\S]+?)%>/g, (match, code) => { // 正常输出 return `' + obj.${code.trim()} + '` }).replace(/<%([\s\S]+?)%>/g, (match, code) => { // 可执行的 return `'; ${code.trim()}; tpl += '` }).replace(/\r\n/g, '').replace(/\n/g, ''); tpl = `tpl='${tpl}';` tpl = `let tpl = ''; with (obj) { ${tpl} } return tpl;` return new Function('obj', 'escape', tpl); } let res = render(complie(str), {users: [1,2,3]})
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论