Nuxt.js 加载第三方广告脚本未完成跳转触发document读取null错误循环
问题原因
- SPA客户端路由特性:Nuxt属于单页应用,页面跳转走的是客户端路由切换,不会触发浏览器整页重载,你在当前组件注入的异步广告脚本加载请求不会被自动终止,等脚本加载完成准备执行时,原组件已经销毁,脚本依赖的DOM节点、全局配置上下文已经被清理,就会抛出找不到对象的错误。
document.write的执行机制问题:当页面DOM解析完成(即DOMContentLoaded事件触发后)调用document.write,浏览器会隐式执行document.open()清空当前整个文档的原有内容;你遇到的无限报错是因为第三方invoke.js内部内置了失败重试逻辑,取不到目标document相关对象就会无限循环执行重试逻辑,所以会每毫秒触发一次报错,直到整页刷新才会终止。
解决方案
推荐两种适配Nuxt场景的可行方案,都可以彻底解决该问题:
方案1:手动注入脚本+组件销毁时清理(无隔离需求时首选)
直接替换掉原有的document.write写法,手动控制脚本的加载和销毁逻辑,代码示例如下:
// Nuxt2 组件内代码 export default { data() { return { adScript: null, adLoaded: false } }, mounted() { if (process.client && !this.adLoaded) { this.injectAdScript() this.adLoaded = true } }, // 组件销毁前清理所有广告相关资源 beforeDestroy() { // 移除未加载完成/已加载的广告脚本 if (this.adScript) { this.adScript.remove() this.adScript = null } // 清理全局广告配置,避免脚本加载后执行逻辑 window.atOptions = null // 移除广告挂载节点 const adContainer = document.getElementById('myDivId') adContainer && adContainer.remove() }, methods: { injectAdScript() { const adContainer = document.getElementById('myDivId') if (!adContainer) return // 先挂载广告要求的全局配置 window.atOptions = { 'key': 'fiajiewfoj3jiosfo', 'format': 'iframe', 'height': 90, 'width': 728, 'params': {} } // 手动创建广告脚本节点 this.adScript = document.createElement('script') this.adScript.type = 'text/javascript' this.adScript.async = true this.adScript.src = `${location.protocol}//script-src.com/12345767890/invoke.js` // 脚本加载错误时自动清理 this.adScript.onerror = () => { this.adScript && this.adScript.remove() } adContainer.appendChild(this.adScript) } } }
方案2:iframe沙箱隔离(兼容性最好,第三方脚本有异常逻辑时首选)
将广告代码完全包裹在独立iframe中执行,和父页面上下文完全隔离,路由切换时直接销毁iframe即可终止所有内部脚本执行,不会影响父页面,代码示例如下:
// Nuxt2 组件内代码 export default { mounted() { if (process.client) { const adContainer = document.getElementById('myDivId') // 创建和广告尺寸匹配的iframe容器 const adIframe = document.createElement('iframe') adIframe.width = '728' adIframe.height = '90' adIframe.frameBorder = '0' adIframe.scrolling = 'no' adIframe.style.border = 'none' adContainer.appendChild(adIframe) // 向iframe内部写入广告代码 const iframeDoc = adIframe.contentDocument || adIframe.contentWindow.document iframeDoc.open() iframeDoc.write(` <script> atOptions = { 'key' : 'fiajiewfoj3jiosfo', 'format' : 'iframe', 'height' : 90, 'width' : 728, 'params' : {} }; </scr` + `ipt> <script src="${location.protocol}//script-src.com/12345767890/invoke.js"></scr` + `ipt> `) iframeDoc.close() // 组件销毁时直接移除iframe,内部所有执行逻辑直接终止 this.$once('hook:beforeDestroy', () => { adIframe.remove() }) } } }
内容的提问来源于stack exchange,提问作者StarPass
相关产品推荐
相关产品推荐

