Template literals 本身不能直接插入 HTML 到页面中,仅生成普通字符串,需配合 innerHTML(须手动转义)、createElement(自动转义)或 template 元素等安全方式使用。

template literals 本身不能直接插入 HTML 到页面中
JavaScript 的 template literals(反引号字符串)只是生成普通字符串,哪怕内容是 HTML 标签,浏览器也不会自动解析渲染。你写 `<div>${name}</div>`,得到的是一个字符串,不是 DOM 节点。
常见错误现象:element.innerHTML = `<p>Hello</p>` 看似“生效”,但若 name 来自用户输入,就可能触发 XSS;更隐蔽的问题是:没做转义时,${userInput} 中的 <script> 会被执行。
- 使用场景:适合快速拼接结构简单、数据完全可信的模板(如内部管理后台的静态预览)
- 不推荐用于用户可控字段的直接插值
- 性能上无额外开销,但安全风险远大于便利性
innerHTML + template literals 是最常用但需手动转义的组合
如果你坚持用 innerHTML,必须自己处理 HTML 转义,否则等于敞开 XSS 大门。原生 JS 没有内置 escapeHTML,得手写或借助小函数:
function escapeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const name = '<script>alert(1)</script>张三';
const html = `<p>姓名:${escapeHTML(name)}</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p>`;
element.innerHTML = html;
注意:这个函数只覆盖常见字符,不处理 Unicode 变体或富文本嵌套。如果字段含链接、样式等复杂内容,innerHTML 就不该是首选。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
更安全的替代方案:用 createElement + append 构建动态结构
绕过字符串拼接,从根本上杜绝 XSS。虽然代码稍长,但逻辑清晰、类型安全、可调试性强:
const item = document.createElement('li');
item.className = 'user-item';
item.textContent = name; // 自动转义文本内容
const avatar = document.createElement('img');
avatar.src = avatarUrl;
avatar.alt = 'avatar';
item.append(avatar);
listElement.append(item);
- 所有动态内容走
textContent或setAttribute,不会被解析为 HTML - 需要设置 HTML 属性(如
innerHTML、src、href)时,确保来源可信或做过校验 - 多次
append比反复赋值innerHTML性能略低,但现代浏览器优化后差异可忽略
复杂模板建议交给原生 <template> 元素或轻量库
当结构超过 3 层嵌套、条件分支多、还要复用时,硬拼 template literals 很快变得难读难维护。原生 <template> 是专为此设计的:
<template id="user-card">
<article class="card">
<h3 class="name"></h3>
<p class="bio"></p>
</article>
</template>
然后用 JS 克隆填充:
const tmpl = document.getElementById('user-card').content;
const clone = document.importNode(tmpl, true);
clone.querySelector('.name').textContent = user.name;
clone.querySelector('.bio').textContent = user.bio;
container.append(clone);
这种方式既保留 HTML 的可读性,又避免字符串拼接的脆弱性;比手写 createElement 更贴近设计稿,也比第三方模板引擎轻量得多。
真正容易被忽略的是:哪怕用了 <template>,只要最后用 innerHTML 填充某个子节点,XSS 风险就立刻回归——安全链条中最弱的一环,决定整体水位。


















