如何将服务端渲染的原生DOM元素传入Vue应用并在插槽中渲染
解决方案
核心思路是直接提取挂载容器的原有原生DOM节点引用,通过渲染函数传入目标组件插槽,全程不涉及HTML字符串解析,完全规避XSS风险。
Vue 2 实现步骤
- 提前提取挂载容器的原有子节点,避免挂载时被直接覆盖丢失
const mountContainer = document.querySelector('.c-search') // 转成数组存储原始DOM节点引用 const originalChildNodes = Array.from(mountContainer.childNodes)
- 挂载时通过渲染函数的子节点参数,将DOM节点传入组件默认插槽
new Vue({ render: (h) => h(NameOfTheComponent, null, originalChildNodes) }).$mount(mountContainer)
- 在
NameOfTheComponent组件模板对应位置添加默认插槽即可渲染原有内容
<!-- NameOfTheComponent 组件内 --> <template> <div class="search-wrapper"> <!-- 其他组件内容 --> <slot /> <!-- 原有服务端渲染的DOM会直接渲染在这里 --> </div> </template>
具名插槽适配
如果需要将不同节点分配到对应具名插槽,可以提前对原有节点做分类:
const slots = { default: [], disclaimer: [] } originalChildNodes.forEach(node => { // 匹配指定类名的节点放入disclaimer插槽 if (node.nodeType === 1 && node.classList.contains('c-search__disclaimer')) { slots.disclaimer.push(node) } else { slots.default.push(node) } }) new Vue({ render: (h) => h(NameOfTheComponent, { scopedSlots: { default: () => slots.default, disclaimer: () => slots.disclaimer } }) }).$mount(mountContainer)
组件内直接使用对应具名插槽即可:
<slot name="disclaimer" />
Vue 3 实现步骤
逻辑和Vue2一致,仅API写法不同:
import { createApp, h } from 'vue' const mountContainer = document.querySelector('.c-search') const originalChildNodes = Array.from(mountContainer.childNodes) createApp({ render: () => h(NameOfTheComponent, null, { default: () => originalChildNodes }) }).mount(mountContainer)
方案优势
- 全程不使用
v-html,没有HTML字符串解析环节,完全规避XSS安全风险,符合Vue官方安全规范 - 原有DOM节点的所有属性、原生事件绑定都会完整保留,不需要额外做属性同步
- 浏览器不会重复执行原有DOM内的script标签,即使CMS内容被植入恶意脚本也不会触发执行,安全性更高
内容的提问来源于stack exchange,提问作者Mikel Wohlschlegel
相关产品推荐
相关产品推荐

