浏览器不识别{{}}和{% %}语法,需自定义解析引擎介入dom构建;直接innerhtml替换会导致事件丢失、光标重置、xss风险及性能下降;正确做法是用+clonenode(true)并仅遍历文本节点替换。

直接在 HTML 中写 {{ user.name }} 或 {% if %} 不会生效——浏览器原生不识别这些语法,必须靠自定义解析引擎介入 DOM 构建流程才能处理。
为什么不能用 innerHTML + 字符串替换
常见错误是拿到模板字符串后直接 el.innerHTML = template.replace(/{{([^}]+)}}/g, ...)。这会导致:
- 所有已绑定的事件监听器丢失(
button.addEventListener失效) -
input光标位置重置、video播放进度清零 - 若
{{ user.name }}含<script></script>片段,直接插入等于执行 XSS - 浏览器强制重建子树,触发多次 layout,性能明显下降
正确起点:用 <template></template> + cloneNode(true)
<template></template> 内容不会被解析或渲染,保留在文档片段中,是唯一安全的模板容器:
- 必须用
template.content.cloneNode(true),不能用template.innerHTML—— 后者会丢掉<slot></slot>和<svg></svg>的命名空间 - 克隆后只遍历文本节点(
node.nodeType === 3),在textContent中做变量替换,绝不碰属性值或事件处理器 - 用户输入内容必须转义:
value.replace(/&/g, '&').replace(/, ',再塞进 <code>document.createTextNode()
如何支持 {% if %} 和 {% for %} 嵌套逻辑
正则无法可靠处理嵌套结构,比如 {% if a %}{% if b %}{% endif %}{% endif %}:
- 第一步:用正则提取所有
{%和%}块,记录起始/结束位置和类型(if、for、endif),生成扁平指令列表 - 第二步:按位置顺序遍历,用栈匹配
if/endif,生成带作用域的 JS 字符串(如if (data.a) { ... }) - 第三步:用
Function('data', 'return `' + compiledTemplate + '`')编译(仅限可信上下文,禁止用于用户上传模板) - 首次编译慢,但结果函数可缓存;重复渲染比每次正则快 5–10 倍
容易被忽略的 DOM 状态细节
即使模板安全克隆并替换完成,挂载时仍可能破坏状态:
- 动态插入节点后,必须显式调用
customElements.upgrade(el),否则connectedCallback不触发 -
connectedCallback不是“初始化钩子”,它会在元素反复进出 DOM 时多次调用,需用this._isRendered标记防重复 - Shadow DOM 必须在
constructor中attachShadow,内容填充要放在connectedCallback或queueMicrotask里 -
attributeChangedCallback不响应 HTML 静态属性,初始值必须在connectedCallback中手动读取:this.getAttribute('name')
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











