如何在Component中动态注入后端返回的HTML与script代码
动态注入含script的HTML内容到组件视图的解决方案
浏览器默认使用innerHTML插入HTML时,不会执行其中的<script>标签,所以除了插入HTML结构,还需要单独处理script标签的加载执行逻辑,以下是通用方案和各主流框架的实现示例:
通用实现步骤
- 先将后端返回的HTML字符串解析为临时DOM结构,提取所有的script标签
- 将剥离了script标签的HTML内容插入到组件的目标容器中
- 逐个处理提取到的script标签:如果是外部src引入的script,动态创建script节点插入到文档中;如果是内联script,直接执行内部代码
各主流框架实现示例
Vue 框架实现
结合v-html指令+手动处理script:
<template> <div ref="embedContainer" v-html="pureHtml"></div> </template> <script setup> import { ref, watch, onMounted, nextTick } from 'vue' const embedContainer = ref(null) const article = ref({ embed_script: '' // 后端返回的embed_script内容 }) const pureHtml = ref('') const injectEmbedContent = (content) => { // 临时DOM节点解析内容 const tempDiv = document.createElement('div') tempDiv.innerHTML = content // 提取所有script标签 const scripts = tempDiv.querySelectorAll('script') // 剥离script后的纯HTML pureHtml.value = tempDiv.innerHTML.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') // 等待HTML渲染完成后处理script nextTick(() => { scripts.forEach(oldScript => { const newScript = document.createElement('script') // 复制所有原script属性 Array.from(oldScript.attributes).forEach(attr => { newScript.setAttribute(attr.name, attr.value) }) // 复制内联脚本内容 if (oldScript.innerHTML) { newScript.innerHTML = oldScript.innerHTML } embedContainer.value.appendChild(newScript) }) }) } onMounted(() => { // 接口请求获取article数据后调用注入方法 injectEmbedContent(article.value.embed_script) }) </script>
React 框架实现
结合dangerouslySetInnerHTML+useRef处理:
import { useEffect, useRef } from 'react' const EmbedComponent = ({ article }) => { const containerRef = useRef(null) useEffect(() => { if (!article?.embed_script || !containerRef.current) return const content = article.embed_script const tempDiv = document.createElement('div') tempDiv.innerHTML = content const scripts = tempDiv.querySelectorAll('script') // 先插入纯HTML containerRef.current.innerHTML = tempDiv.innerHTML.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') // 处理script标签 scripts.forEach(oldScript => { const newScript = document.createElement('script') Array.from(oldScript.attributes).forEach(attr => { newScript.setAttribute(attr.name, attr.value) }) if (oldScript.innerHTML) newScript.innerHTML = oldScript.innerHTML containerRef.current.appendChild(newScript) }) // 组件卸载时清理内容避免内存泄漏 return () => { if (containerRef.current) { containerRef.current.innerHTML = '' } } }, [article]) return <div ref={containerRef}></div> } export default EmbedComponent
Angular 框架实现
结合DomSanitizer处理安全上下文后手动处理script:
import { Component, Input, ElementRef, OnInit, OnDestroy } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'app-embed', template: '<div [innerHTML]="pureHtml"></div>' }) export class EmbedComponent implements OnInit, OnDestroy { @Input() article: any; pureHtml: any; constructor( private sanitizer: DomSanitizer, private el: ElementRef ) {} ngOnInit(): void { if (!this.article?.embed_script) return const content = this.article.embed_script const tempDiv = document.createElement('div') tempDiv.innerHTML = content const scripts = tempDiv.querySelectorAll('script') // 清理script后消毒HTML const rawHtml = tempDiv.innerHTML.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') this.pureHtml = this.sanitizer.bypassSecurityTrustHtml(rawHtml) // 渲染完成后处理script setTimeout(() => { const container = this.el.nativeElement.querySelector('div') scripts.forEach(oldScript => { const newScript = document.createElement('script') Array.from(oldScript.attributes).forEach(attr => { newScript.setAttribute(attr.name, attr.value) }) if (oldScript.innerHTML) newScript.innerHTML = oldScript.innerHTML container.appendChild(newScript) }) }, 0) } ngOnDestroy(): void { this.el.nativeElement.innerHTML = '' } }
注意:如果后端返回的内容来源不可信,直接注入script会存在XSS安全风险,请确保仅注入信任的后端返回内容,必要时可对返回的HTML做标签白名单过滤。
内容的提问来源于stack exchange,提问作者Kerby82
相关产品推荐
相关产品推荐

