js实现模板引擎
实现模板引擎的基本思路
模板引擎的核心是将模板字符串中的占位符替换为实际数据。常见的占位符格式如{{name}},通过正则表达式匹配替换。
简单字符串替换
使用正则表达式匹配占位符并用实际数据替换:
function render(template, data) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => data[key] || '');
}
const template = 'Hello, {{name}}!';
const data = { name: 'World' };
console.log(render(template, data)); // 输出: Hello, World!
支持嵌套对象
扩展正则表达式以支持嵌套对象路径如{{user.name}}:
function render(template, data) {
return template.replace(/\{\{([\w\.]+)\}\}/g, (match, path) => {
const keys = path.split('.');
return keys.reduce((obj, key) => (obj && obj[key]) || '', data);
});
}
const template = 'Hello, {{user.name}}!';
const data = { user: { name: 'Alice' } };
console.log(render(template, data)); // 输出: Hello, Alice!
支持条件语句
通过自定义语法实现简单的条件判断:
function render(template, data) {
const ifPattern = /\{\{if\s+(\w+)\}\}(.*?)\{\{\/if\}\}/gs;
return template.replace(ifPattern, (match, key, content) =>
data[key] ? render(content, data) : ''
);
}
const template = '{{if show}}Hello!{{/if}}';
const data = { show: true };
console.log(render(template, data)); // 输出: Hello!
支持循环语句
实现数组遍历功能:
function render(template, data) {
const loopPattern = /\{\{each\s+(\w+)\}\}(.*?)\{\{\/each\}\}/gs;
return template.replace(loopPattern, (match, key, content) =>
data[key].map(item => render(content, item)).join('')
);
}
const template = '{{each items}}<li>{{name}}</li>{{/each}}';
const data = { items: [{name: 'A'}, {name: 'B'}] };
console.log(render(template, data)); // 输出: <li>A</li><li>B</li>
安全转义
防止XSS攻击,对输出进行HTML转义:
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function render(template, data, safe = true) {
return template.replace(/\{\{(\w+)\}\}/g, (match, key) =>
safe ? escapeHtml(data[key] || '') : data[key] || ''
);
}
完整示例
结合所有功能的简化实现:
class TemplateEngine {
constructor() {
this.cache = {};
}
compile(template) {
const fnBody = `with(obj){\nreturn \`${template
.replace(/\{\{([\w\.]+)\}\}/g, '${$1}')
.replace(/\{\{if\s+(\w+)\}\}(.*?)\{\{\/if\}\}/gs, '${$1 ? `$2` : ``}')
.replace(/\{\{each\s+(\w+)\}\}(.*?)\{\{\/each\}\}/gs, '${$1.map(i=>`$2`).join(``)}')
}\`;\n}`;
return new Function('obj', fnBody);
}
render(template, data) {
const fn = this.compile(template);
return fn(data);
}
}
const engine = new TemplateEngine();
const result = engine.render(
'{{each users}}{{if show}}<div>{{name}}</div>{{/if}}{{/each}}',
{ users: [{name: 'Alice', show: true}], show: true }
);
console.log(result); // 输出: <div>Alice</div>
性能优化建议
对于重复使用的模板,可进行预编译缓存。避免每次渲染都重新解析模板字符串。

class TemplateEngine {
constructor() {
this.cache = {};
}
compile(template) {
if (!this.cache[template]) {
// 编译逻辑...
this.cache[template] = compiledFunction;
}
return this.cache[template];
}
}






