← 返回题目列表

如何手写简单模板字符串渲染函数?

中等 第 25 / 27 题 更新于 2026/07/29
手写代码模板引擎字符串安全

简化版

简单模板渲染函数用于把 Hello {{ user.name }} 替换成数据里的值。实现思路是用正则匹配 {{ path }},取出路径后从数据对象里读取值,再替换回字符串。边界包括路径不存在、空值、转义、安全 XSS、是否允许表达式。面试版建议只支持属性路径,不执行任意 JS 表达式。

详细版

基础实现可以结合路径 get。

模板数据结果
Hi {{name}}{ name: "Tom" }Hi Tom
{{user.age}}{ user: { age: 18 } }18
{{missing}}{}空字符串
function get(obj: any, path: string) {
  return path.split('.').reduce((cur, key) => cur == null ? undefined : cur[key], obj)
}

function render(template: string, data: Record<string, any>) {
  return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, path) => {
    const value = get(data, path)
    return value == null ? '' : String(value)
  })
}

手写模板时不要为了“强大”直接 eval,表达式执行会带来明显安全风险。

完整版教学

一、题目考察点

这题考正则、字符串替换、对象路径读取和安全边界。它看起来像玩具题,但能引出模板引擎的基本思想。

面试中一般不要求实现完整 Vue/Mustache,只要支持变量占位即可。

二、模板占位符怎么匹配

常见占位符是 {{ name }}。可以用正则匹配:

const reg = /\{\{\s*([\w.]+)\s*\}\}/g

含义是匹配双大括号,中间允许空格,捕获由字母数字下划线和点组成的路径。

三、如何读取路径

如果只支持 user.name,可以按点拆分。

function getByPath(obj: any, path: string) {
  const keys = path.split('.')
  let cur = obj
  for (const key of keys) {
    if (cur == null) return undefined
    cur = cur[key]
  }
  return cur
}

不存在时返回 undefined,渲染时转成空字符串。

四、为什么不建议 eval

有些人会想支持 {{ user.age + 1 }},然后用 evalnew Function。这会让模板可以执行任意代码。

如果模板内容来自用户或 CMS,就可能造成 XSS 或远程代码执行风险。

面试版可以明确说:只支持安全的属性路径,不支持表达式。

五、HTML 转义问题

如果渲染结果要插入 HTML,需要考虑转义。

function escapeHtml(value: string) {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

否则用户输入 <script> 可能被当成 HTML 执行。

六、扩展能力

完整模板引擎还可能支持:

  • 条件判断。
  • 循环列表。
  • 过滤器。
  • HTML 转义和非转义输出。
  • 编译缓存。
能力面试基础版完整模板引擎
变量替换支持支持
表达式不建议受控支持
安全转义可加必须设计
编译优化不需要常见

七、常见误区与追问

  • 误区:模板渲染直接 replace 就完全够。 还要考虑路径不存在、转义、安全和重复占位符。
  • 误区:用 eval 支持表达式更高级。 任意代码执行风险很高,面试中应主动规避。
  • 误区:null 应该渲染成字符串 null。 多数模板会把 null/undefined 渲染为空字符串,具体要约定。
  • 追问:如何防 XSS? 插入 HTML 前对动态值做 HTML 转义,或使用安全 DOM API。
  • 追问:如何提高重复渲染性能? 可以先编译模板为 token 或函数,并缓存编译结果。
  • 追问:如何支持数组路径? 复用更完整的 getByPath,支持 items[0].name

八、加强记忆

模板渲染记成“匹配占位符、读取路径、替换字符串”。工程答案要补一句:默认只支持安全路径,不执行任意表达式,插入 HTML 要转义。