模板本身不响应数据变更,因其为单次求值结构;HTML模板(如、EJS等)渲染后即固化,数据更新必须由外部逻辑显式触发重渲染,无法自动响应。

模板本身不响应数据变更——所有 HTML 模板(<template></template>、EJS、Nunjucks、Thymeleaf 等)都是单次求值结构,渲染完即固化。所谓“数据源变更后重渲染”,必须由外部逻辑显式触发,不能指望模板自动更新。
为什么直接改 data 对象不会让 template 再次生效
<template></template> 是离线 DocumentFragment,内容只在克隆时被读取一次;服务端模板(如 EJS)则在 res.render() 调用时完成字符串拼接,之后与 JS 数据完全解耦。常见错误现象包括:
- 改了
user.name,但已插入 DOM 的克隆节点里文字没变 - 服务端返回的 HTML 里写
<span></span>,后续前端 JS 改user.name,页面不动 - 用
innerHTML = template.innerHTML试图“刷新”,结果报错或内容为空(因为template.innerHTML永远是空字符串)
服务端模板如何应对数据源变更
服务端没有“响应式”概念,只能靠重新调用渲染函数 + 重发 HTML。关键在于控制重渲染的触发时机和范围:
- 数据库变更 → 触发缓存 key 失效(如
fragment:product:123),下次请求走新数据渲染 - API 接口返回新数据 → 必须调用
renderToString()(React)或thymeleaf.process()(Spring)等服务端 API,不能只返回 JSON - 避免在模板中嵌套异步逻辑:比如
里再发 HTTP 请求,会阻塞整个响应流 - 若需部分刷新,应拆成独立 endpoint(如
/api/product/123/header),返回纯 HTML 片段,供前端用replaceChildren()替换
客户端 template 标签怎么安全地“重渲染”
原生 <template></template> 不提供 update 接口,每次都要走完整克隆 → 填值 → 插入流程。容易踩的坑:
立即学习“前端免费学习笔记(深入)”;
- 重复使用
template.content:第一次cloneNode(true)后,template.content仍可访问,但第二次cloneNode(true)仍有效——它不是“消耗型”资源,但内容不会自动更新 - 填值时误用
innerHTML:比如el.innerHTML = data.title,若data.title含 HTML 字符,可能 XSS;若含事件监听器,会被销毁 - 没处理重复 ID:克隆多个实例后,
id="close-btn"出现多次,document.getElementById('close-btn')只返回第一个 - 表格插入目标错位:把
<tr><td>xxx</td></tr>直接appendChild到<table>,浏览器会自动补 <code><tbody>,导致 DOM 结构错乱;必须插到 <code><tbody> 或 <code><thead> <h3>真正需要关注的边界点</h3> <p>数据源变更是否跨 hydration 边界——这是最容易被忽略的复杂点。比如服务端已渲染好一个商品列表,前端又用 fetch 拉取新数据并用 <code><template></template>插入,这部分 DOM 完全脱离 React/Vue 的管控:状态不共享、事件不自动绑定、key 冲突不校验。此时你面对的不是“模板怎么重渲染”,而是“两套渲染机制如何共存”。解决方案从来不是修补模板,而是统一入口:要么全 SSR + 客户端接管,要么全客户端驱动,中间态最易出问题。



















