相同的HTML代码,用v-html与直接写在模板中染相表现差异巨大,如何规避?

可以通过下面的链接查看问题,template里面的html与v-html是一样的代码,但是表现差异巨大
DEMO

阅读 1.6k
avatarAI BotBETA

在使用 Vue.js 的 v-html 指令时,你确实可能会遇到与直接在模板中编写 HTML 不同的表现。这通常是因为 v-html 会直接渲染原始的 HTML 字符串,而 Vue 模板中的 HTML 会经过 Vue 的编译和虚拟 DOM 的处理。这种差异可能由以下几个原因引起:

  1. CSS 样式作用域:直接在模板中编写的 HTML 会受到组件内部或全局 CSS 的影响,而 v-html 插入的 HTML 可能不会(除非这些样式是全局的或具有足够的选择器优先级来覆盖)。
  2. Vue 的响应性v-html 插入的 HTML 是静态的,它不会响应 Vue 实例的数据变化。而模板中的 HTML 可以包含 Vue 指令(如 v-ifv-for 等),这些指令会根据 Vue 实例的数据变化来动态地更新 DOM。
  3. 安全性:使用 v-html 插入不受信任的 HTML 可能会带来 XSS(跨站脚本攻击)的风险,因为它会执行 HTML 中的 JavaScript 代码。而直接在模板中编写的 HTML 则是安全的,因为 Vue 会对其进行适当的转义。

为了规避这些差异,你可以考虑以下策略:

  • 确保样式的一致性:确保 v-html 插入的 HTML 使用的样式与组件内部或全局样式一致。你可以通过检查 CSS 选择器的优先级和范围来实现这一点。
  • 使用 Vue 组件:如果可能,将需要动态渲染的 HTML 封装成 Vue 组件,并通过 props 传递数据给这些组件。这样可以保持 Vue 的响应性和安全性,并且可以更容易地控制样式和行为。
  • 清理和过滤 HTML:如果你必须从外部源获取 HTML,请确保对其进行清理和过滤,以去除潜在的恶意代码和不必要的标签。你可以使用 DOM 解析库(如 DOMPurify)来清理 HTML。
  • 避免使用 v-html:如果可能,尽量避免使用 v-html。相反,使用 Vue 的数据绑定和指令来动态渲染内容。这不仅可以提高安全性,还可以保持代码的清晰和可维护性。

综上所述,通过确保样式一致性、使用 Vue 组件、清理和过滤 HTML 以及避免使用 v-html,你可以规避 v-html 与直接在模板中编写 HTML 之间的表现差异。

2 个回答

将 vue 模板中 svg 的 viewbox 属性修改为 viewBox 即可渲染一致

和编写 tempaltev-html 没有关系。
只是因为你的 viewBox 拼写错了,修改成 viewBox 就行了。

<script setup>

const html = `<section><section><section style="margin: 10px auto; display: flex; justify-content: center; padding-bottom: 5px; box-sizing: border-box;"><section style="display: flex; flex-direction: column;"><section class="assistant" style="width: 35px; margin-left: auto; box-sizing: border-box;"><img src="https://oss.tccms.cn/1sqnkqGnuBk" draggable="false" class="assistant" style="width: 100%; display: block; vertical-align: baseline; box-sizing: border-box; max-width: 100% !important;"></section><section style="display: flex; flex-direction: column; margin: 0px 20px; z-index: 3;"><section style="font-size: 16px; color: rgb(255, 255, 255); text-align: center; z-index: 4;"><strong>Haha</strong></section><section style="width: 150px; margin: -43px auto 0px; box-sizing: border-box;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 219 81" xml:space="default" style="display: block;"><g id="图层_2"><g id="图层_1-2"><polygon points="212 9 207 0 18 9 9 10 0 59 18 81 207 81 219 22 211.91 9 212 9" style="fill: rgb(112, 204, 251); fill-rule: evenodd;"></polygon><path d="M13.71,47.59a5,5,0,1,1-5,5A5,5,0,0,1,13.71,47.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path><path d="M16.71,31.59a5,5,0,1,1-5,5A5,5,0,0,1,16.71,31.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path><path d="M19.71,15.59a5,5,0,1,1-5,5A5,5,0,0,1,19.71,15.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path></g></g></svg></section></section><section style="width: 140px; margin-top: -40px; box-sizing: border-box;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 254.17 85.67" xml:space="default" style="display: block;"><g id="图层_2"><g id="图层_1-2"><path d="M16,0A16.28,16.28,0,0,0,0,16.53C0,25.66,16,42,16,42S32,25.66,32,16.53A16.27,16.27,0,0,0,16,0Zm0,22.91a7.72,7.72,0,1,1,7.68-7.72A7.69,7.69,0,0,1,16,22.91Z" style="fill: rgb(255, 216, 96); fill-rule: evenodd;"></path><path d="M17,42S-15.36,88.39,76,68c57.05-12.73,111.36-8.48,89,5-27,16.29,34.67,17.14,89-3" style="fill: none; stroke: rgb(51, 51, 51); stroke-dasharray: 6, 4;"></path></g></g></svg></section></section></section></section><section><p><br></p></section></section>`

</script>

<template>
  <section><section><section style="margin: 10px auto; display: flex; justify-content: center; padding-bottom: 5px; box-sizing: border-box;"><section style="display: flex; flex-direction: column;"><section class="assistant" style="width: 35px; margin-left: auto; box-sizing: border-box;"><img src="https://oss.tccms.cn/1sqnkqGnuBk" draggable="false" class="assistant" style="width: 100%; display: block; vertical-align: baseline; box-sizing: border-box; max-width: 100% !important;"></section><section style="display: flex; flex-direction: column; margin: 0px 20px; z-index: 3;"><section style="font-size: 16px; color: rgb(255, 255, 255); text-align: center; z-index: 4;"><strong>Haha</strong></section><section style="width: 150px; margin: -43px auto 0px; box-sizing: border-box;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 219 81" xml:space="default" style="display: block;"><g id="图层_2"><g id="图层_1-2"><polygon points="212 9 207 0 18 9 9 10 0 59 18 81 207 81 219 22 211.91 9 212 9" style="fill: rgb(112, 204, 251); fill-rule: evenodd;"></polygon><path d="M13.71,47.59a5,5,0,1,1-5,5A5,5,0,0,1,13.71,47.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path><path d="M16.71,31.59a5,5,0,1,1-5,5A5,5,0,0,1,16.71,31.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path><path d="M19.71,15.59a5,5,0,1,1-5,5A5,5,0,0,1,19.71,15.59Z" style="fill: rgb(255, 255, 255); fill-rule: evenodd;"></path></g></g></svg></section></section><section style="width: 140px; margin-top: -40px; box-sizing: border-box;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 254.17 85.67" xml:space="default" style="display: block;"><g id="图层_2"><g id="图层_1-2"><path d="M16,0A16.28,16.28,0,0,0,0,16.53C0,25.66,16,42,16,42S32,25.66,32,16.53A16.27,16.27,0,0,0,16,0Zm0,22.91a7.72,7.72,0,1,1,7.68-7.72A7.69,7.69,0,0,1,16,22.91Z" style="fill: rgb(255, 216, 96); fill-rule: evenodd;"></path><path d="M17,42S-15.36,88.39,76,68c57.05-12.73,111.36-8.48,89,5-27,16.29,34.67,17.14,89-3" style="fill: none; stroke: rgb(51, 51, 51); stroke-dasharray: 6, 4;"></path></g></g></svg></section></section></section></section><section><p><br></p></section></section>
  <div v-html="html" />
</template>

至于为什么,看起来是因为 v-html 给你的 svg 元素添加HTML属性时,使用 setAttribute() 方法导致的。该方法会将其属性名称自动转换为全小写形式这样来设置目标元素的 HTML 属性,正好规避掉了你的 viewbox 大小写错误的问题。


修正

从当前表现来看使用 v-html 的内容渲染是正确的 viewBox 属性。
而在 template 中编辑的内容渲染的是错误的 viewbox 属性。

按照 @42 的提醒。检查了一下使用 innerHTML 时浏览器会自动识别并处理 svg 元素的 XML 属性,把拼写错误的 viewbox 转换成正确的 viewBox

而使用 template 中的 svg 内容会按照编写时错误的 viewbox 保持拼写错误拼写作为 props 值传递下去在创建 VNode 时作为 HTML attributes 值使用。
但浏览器不会自动处理 svg 元素的属性名称大小写错误。 因为 svg 也不是 HTML 元素 setAttribute 方法也不会自动处理转换成小写。

这两处的自动处理没有统一我倒是挺意外的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏