不能用 Vue.filter 安全解码 HTML 实体,因其在小程序和 App 端不执行,H5 端还可能引发 XSS;应改用 DOMParser 或 textarea 安全解码,并配合白名单过滤后使用 v-html 或插值渲染。

不能用 Vue.filter 安全解码 HTML 实体——它在小程序和 App 端根本不会执行,H5 端还可能引发 XSS。
为什么 v-html + 过滤器组合是危险且无效的
常见写法 {{ content | htmlDecode }} + v-html 渲染,看似合理,实则踩了两个坑:
-
Vue.filter在微信/支付宝/字节等小程序中被编译器直接忽略,模板里写再多次也无输出 - 即使 H5 端生效,
htmlDecode若只是简单 replace(如→),没做上下文隔离,传入恶意字符串如<script>alert(1)</script>会被原样插入 DOM,触发 XSS - uni-app 的
v-html不会自动解码实体,它只做原样插入;你得先解码再给它,但解码动作本身必须可控
真正可用的解码方式:用 DOMParser 或 textarea 安全还原
不依赖过滤器,也不信任正则替换。核心思路是:借浏览器原生机制解码,再取纯文本或受控 HTML。
- 解码后仅需显示纯文本?用
textarea最稳:const decoded = document.createElement('textarea'); decoded.innerHTML = rawString; return decoded.value; - 需要保留部分标签(如
<b>、<p>)?用DOMParser解析后再白名单过滤:const doc = new DOMParser().parseFromString(rawString, 'text/html');,然后遍历节点,只保留allowedTags = ['b', 'i', 'p', 'br']中的元素 - 小程序平台不支持
DOMParser,但支持textarea—— 所有端都可用该 fallback 方案 - 别在
filters/index.js里封装这个逻辑,而是写成工具函数,例如safeHtmlDecode(str),在setup()或computed中调用
在模板里怎么安全使用
绝对不要写 v-html="{{ content | htmlDecode }}"。正确姿势是:
立即学习“前端免费学习笔记(深入)”;
- 在
setup()中提前解码:const decodedContent = safeHtmlDecode(props.content) - 若需渲染带格式的 HTML,用
v-html="decodedContent",但确保decodedContent已经过白名单清洗(比如移除了onerror、javascript:等危险属性) - 若只是展示文本,直接用插值:
{{ decodedContent }},天然防 XSS - 后端返回的富文本字段,建议约定统一加前缀如
html:,前端据此决定走v-html还是纯文本渲染,避免误判
最易被忽略的是:解码不是目的,可控才是。哪怕用了 DOMParser,没做属性过滤,照样能执行内联事件。别省那几行清洗代码。

















