标准答案

  1. 先定义模板语法,例如 {{ user.name }} 表示读取 data.user.name。
  2. 用正则或解析器找到模板中的占位符。
  3. 根据占位符路径从数据对象中取值,找不到时返回空字符串或保留原文。
  4. 输出到 HTML 时要考虑转义,避免把用户输入直接插入成可执行 HTML。
  5. 复杂模板引擎通常会把模板编译成 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 执行,扩大安全风险。
  • 没有说明基础变量插值和完整模板引擎之间还有条件、循环、作用域和编译优化差异。

作者信息