标准答案
- 先定义模板语法,例如 {{ user.name }} 表示读取 data.user.name。
- 用正则或解析器找到模板中的占位符。
- 根据占位符路径从数据对象中取值,找不到时返回空字符串或保留原文。
- 输出到 HTML 时要考虑转义,避免把用户输入直接插入成可执行 HTML。
- 复杂模板引擎通常会把模板编译成 render 函数,提高多次渲染性能。
题目解析
手写简单模板引擎可以从变量替换开始,不要一上来实现完整 Vue 或 Handlebars。核心是把模板文本、占位符和数据对象之间的映射关系讲清楚。
安全边界很重要。如果模板结果会进入 innerHTML,那么用户输入必须经过 HTML 转义。否则一个普通昵称字段也可能变成脚本注入入口。
当模板需要频繁渲染时,直接每次正则替换会重复解析模板。真实引擎常把模板预编译成函数,再传入数据执行。
代码示例
下面是只支持变量插值的最小版本。
JavaScript
function getByPath(data, path) {
return path.split('.').reduce((value, key) => {
return value == null ? undefined : value[key]
}, data)
}
function render(template, data) {
return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, path) => {
const value = getByPath(data, path)
return value == null ? '' : String(value)
})
}
render('Hello {{ user.name }}', { user: { name: 'Luo' } })常见误区
- 只做字符串替换,不考虑 HTML 转义和 XSS 风险。
- 把任意表达式直接交给 eval 或 new Function 执行,扩大安全风险。
- 没有说明基础变量插值和完整模板引擎之间还有条件、循环、作用域和编译优化差异。