Vue中实现带特殊样式文本循环展示,是否有优于v-html的方案?
更优实现方案:替代v-html处理带样式的文本展示
你当前用v-html的方式虽然能实现需求,但存在两个明显问题:一是HTML字符串硬编码维护性差,二是有XSS注入风险(如果数据来自用户输入或第三方)。这里给你几个更优的实现方案:
方案1:用结构化数据替代HTML字符串(最推荐)
把需要高亮的内容从HTML字符串拆成结构化数据,比如每个条目包含原始文本和需要高亮的关键词,既安全又便于维护,还能直接配合Vue的响应式特性。
<script setup> import { ref } from "vue"; // 结构化数据:存储原始文本+需要高亮的关键词 const data3 = [ { text: "An approachable, performant and versatile framework for building web user interfaces.", highlight: ["performant"] }, { text: "The library for web and native user interfaces", highlight: ["native"] } ]; // 拆分文本为普通/高亮片段的工具函数 const splitText = (text, highlights) => { if (!highlights.length) return [{ text, highlight: false }]; const regex = new RegExp(`(${highlights.join('|')})`, 'gi'); return text.split(regex) .map(part => ({ text: part, highlight: highlights.some(word => part.toLowerCase() === word.toLowerCase()) })) .filter(part => part.text); // 过滤拆分产生的空字符串 }; </script> <template> <div class="data3"> <p v-for="(item, index) in data3" :key="index"> <template v-for="(segment, idx) in splitText(item.text, item.highlight)" :key="idx"> <span v-if="segment.highlight" class="highlight">{{ segment.text }}</span> <span v-else>{{ segment.text }}</span> </template> </p> </div> </template> <style scoped> .highlight { font-weight: 700; } </style>
方案2:安全处理HTML字符串(适配已有HTML数据场景)
如果数据确实是HTML格式(比如来自后端接口),不要直接用v-html,先通过DOMPurify过滤掉危险标签和属性,彻底规避XSS风险。
- 先安装依赖:
npm install dompurify
- 组件中使用:
<script setup> import { ref, computed } from "vue"; import DOMPurify from 'dompurify'; const data2 = [ "An approachable, <b>performant</b> and versatile framework for building web user interfaces.", "The library for web and <b>native</b> user interfaces", ]; // 计算属性返回过滤后的安全HTML const safeData2 = computed(() => data2.map(item => DOMPurify.sanitize(item))); </script> <template> <div class="data2"> <p v-for="(item, index) in safeData2" :key="index" v-html="item"></p> </div> </template> <style scoped> :deep(.data2 > b) { font-weight: 700; } </style>
方案3:封装高亮组件(适配多场景复用)
如果多个页面需要类似的高亮文本展示,可以封装独立组件,提高代码复用性:
<!-- HighlightText.vue --> <script setup> import { computed } from "vue"; const props = defineProps({ text: String, highlights: { type: Array, default: () => [] } }); const segments = computed(() => { if (!props.highlights.length) return [{ text: props.text, highlight: false }]; const regex = new RegExp(`(${props.highlights.join('|')})`, 'gi'); return props.text.split(regex) .map(part => ({ text: part, highlight: props.highlights.some(word => part.toLowerCase() === word.toLowerCase()) })) .filter(part => part.text); }); </script> <template> <span> <template v-for="(seg, idx) in segments" :key="idx"> <span v-if="seg.highlight" class="highlight">{{ seg.text }}</span> <span v-else>{{ seg.text }}</span> </template> </span> </template> <style scoped> .highlight { font-weight: 700; } </style>
父组件中调用:
<script setup> import { ref } from "vue"; import HighlightText from './HighlightText.vue'; const data3 = [ { text: "An approachable, performant and versatile framework for building web user interfaces.", highlight: ["performant"] }, { text: "The library for web and native user interfaces", highlight: ["native"] } ]; </script> <template> <div class="data3"> <p v-for="(item, index) in data3" :key="index"> <HighlightText :text="item.text" :highlights="item.highlight" /> </p> </div> </template>
方案对比
| 方案类型 | 优点 | 适用场景 |
|---|---|---|
| 结构化数据方案 | 无XSS风险、易维护、样式无需穿透scoped | 新开发项目,可控数据结构 |
| 安全HTML方案 | 适配已有HTML数据、降低XSS风险 | 数据来自后端且为HTML格式 |
| 组件化方案 | 高复用性、代码整洁 | 多页面需要同类高亮展示场景 |
| 原v-html方案 | 实现简单 | 静态本地数据、无安全风险场景 |
内容的提问来源于stack exchange,提问作者JackFan
相关产品推荐
相关产品推荐

