React使用dangerouslySetInnerHTML注入HTML时调用JS函数报未定义问题
报错原因
javascript: 伪协议内的代码执行时,仅会在全局window作用域下查找依赖的函数。React组件内部定义的方法属于ES模块/组件的局部作用域,默认不会挂载到全局window对象上,因此点击链接时会触发showvideo is not defined的引用错误。
解决方案
由于注入的遗留HTML无法修改,改动最小、兼容性最好的方案是:在组件挂载阶段将需要被javascript:协议调用的方法临时挂载到window全局,组件卸载时删除对应全局变量,避免全局命名污染和内存泄漏。
函数组件实现示例
import { useEffect, useRef } from 'react'; export default function CmsContent() { // 用ref存储最新的函数逻辑,避免闭包捕获旧的state/props const showvideoRef = useRef(); showvideoRef.current = (url, width, height, id) => { console.log('触发视频播放', url, width, height, id); // 此处编写视频弹窗、播放等实际业务逻辑 }; useEffect(() => { // 组件挂载时将方法挂到全局 window.showvideo = (...args) => showvideoRef.current(...args); // 组件卸载时清理全局挂载 return () => { delete window.showvideo; }; }, []); // 模拟接口返回的动态HTML内容 const dynamicHtml = `<p><a href="javascript:showvideo('http://urldir/vid.mp4',640,480,0)">Launch vid.</a></p>`; return <div dangerouslySetInnerHTML={{ __html: dynamicHtml }} />; }
类组件实现示例
import React from 'react'; class CmsContent extends React.Component { showvideo(url, width, height, id) { console.log('触发视频播放', url, width, height, id); // 业务逻辑 } componentDidMount() { // 挂载时绑定this保证函数内可访问组件实例属性、方法 window.showvideo = this.showvideo.bind(this); } componentWillUnmount() { delete window.showvideo; } render() { const dynamicHtml = `<p><a href="javascript:showvideo('http://urldir/vid.mp4',640,480,0)">Launch vid.</a></p>`; return <div dangerouslySetInnerHTML={{ __html: dynamicHtml }} />; } }
注意事项
- 挂载到全局的方法名不要和页面已有的其他全局变量重名,避免覆盖原有逻辑
- 如果注入的HTML里有多个需要全局调用的方法,统一在挂载/卸载生命周期里做注册和清理即可
- 不推荐注入HTML后再手动遍历DOM绑定事件的方案,动态内容更新后需要重新绑定,维护成本更高
内容的提问来源于stack exchange,提问作者benishky
相关产品推荐
相关产品推荐

