如何在页面指定位置插入HTML且不额外添加div(支持SSR)
首先说明为什么你用<template v-html="myHtml"></template>不行:Vue的<template>标签只是编译阶段的占位符,不会被渲染成真实的DOM元素,而v-html指令必须绑定到真实DOM元素上才能生效——它本质是通过操作元素的innerHTML来插入内容的,所以模板标签根本承载不了这个指令。
下面给你几个可行的方案,都满足无额外容器(或视觉上无影响)、支持SSR的要求:
方案1:用<span>+display: contents(Vue2/3通用)
这个方案最简单,虽然DOM里会多一个<span>,但display: contents会让浏览器完全忽略这个元素的布局作用,视觉和布局上跟没有额外容器完全一样,而且完美支持SSR:
<span v-html="myHtml" style="display: contents;"></span>
方案2:Vue3专属:渲染函数+HTML解析(纯无容器)
如果连<span>都不想出现在DOM里,可以用Vue3的Fragment特性,配合HTML字符串解析成虚拟节点数组的方式,完全没有额外容器:
首先需要一个纯字符串解析HTML的工具(比如@vueuse/core的useHtmlParser,它不依赖document,支持SSR),然后写一个组件:
<script setup> import { h, Fragment } from 'vue' import { useHtmlParser } from '@vueuse/core' const props = defineProps({ html: { type: String, required: true } }) // 将HTML字符串解析为Vue虚拟节点数组 const htmlNodes = useHtmlParser(props.html) </script> <template> <component :is="Fragment" v-for="node in htmlNodes" :key="node.key" v-bind="node.props"> {{ node.children }} </component> </template>
或者更简洁的渲染函数写法:
<script setup> import { h, Fragment } from 'vue' import { useHtmlParser } from '@vueuse/core' const props = defineProps({ html: String }) const render = () => h(Fragment, {}, useHtmlParser(props.html)) </script> <template> <render /> </template>
这个方案不管是SSR还是客户端渲染,最终DOM里都不会有任何额外容器,完全是你插入的HTML内容。
方案3:Vue2专属:函数式组件兼容SSR
Vue2不支持多根节点,所以需要用函数式组件区分SSR和客户端渲染逻辑:
Vue.component('HtmlRenderer', { functional: true, props: { html: { type: String, required: true } }, render(h, context) { // 客户端渲染:解析HTML为虚拟节点数组 if (process.client) { const div = document.createElement('div') div.innerHTML = context.props.html // 将DOM节点转换为Vue虚拟节点 const convertNode = node => { if (node.nodeType === Node.TEXT_NODE) { return node.textContent } const attrs = {} Array.from(node.attributes).forEach(attr => { attrs[attr.name] = attr.value }) return h(node.tagName.toLowerCase(), attrs, Array.from(node.childNodes).map(convertNode)) } return Array.from(div.childNodes).map(convertNode) } // SSR渲染:直接输出HTML字符串,不需要容器 return context.props.html } })
然后在模板里直接用:
<HtmlRenderer :html="myHtml" />
这个方案在SSR时会直接把HTML字符串输出到页面,没有额外容器;客户端渲染时会把HTML解析成Vue虚拟节点,最终DOM也没有额外容器。
注意事项
不管用哪个方案,都要确保你插入的HTML内容是可信的——v-html和类似的HTML插入方式会直接执行内容里的脚本,存在XSS攻击风险,绝对不要插入用户提交的未过滤内容。
内容的提问来源于stack exchange,提问作者yaquawa

