Next.js App Router嵌入脚本后页面跳转组件消失问题求助
问题解决:Next.js 15.3.2中CherryWidget路由返回后消失的问题
问题重现
在Next.js 15.3.2应用中嵌入CherryWidget脚本,初次加载组件正常工作,但通过客户端路由跳转后返回原页面,组件消失并抛出错误:
Uncaught Error: Widget didn't find LoaderObject for instance [cherry-widget-script]. The loading script was either modified, no call to 'init' method was done or there is conflicting object defined in `window.cherry-widget-script` .
核心原因
- Next.js客户端路由(
Link跳转)为SPA模式,页面切换不会刷新整个页面,脚本加载后状态可能在路由切换时丢失 - 组件挂载时仅初始化一次
window._hw,路由返回时组件重新挂载,但脚本已加载过,init逻辑未正确触发,且window上的冲突对象未清理 Script组件的afterInteractive策略在路由切换时不会自动重新执行onLoad逻辑
解决方案
方案一:路由监听+实例清理(基于Next.js Script组件)
修改CherryWidget.jsx,通过监听路由事件确保每次页面返回时重新初始化组件,并清理旧实例避免冲突:
import Script from 'next/script' import { useEffect } from 'react' import { useRouter } from 'next/router' export default function CherryWidget() { const router = useRouter() useEffect(() => { // 初始化_hw队列 window._hw = window._hw || function() { (window._hw.q = window._hw.q || []).push(arguments) } // 路由切换完成后重新初始化组件 const handleRouteChange = () => { // 优先销毁旧实例(若widget提供destroy方法) window._hw?.("destroy") // 重新执行init window._hw("init", { debug: true, // 开启debug方便排查 variables: { slug: 'hellovet-llc', name: 'HelloVet LLC', imageCategory: 'Veterinary', }, styles: { primaryColor: '#3b56a3', secondaryColor: '#3b56a310', fontFamily: 'open sans', headerFontFamily: 'raleway', }, }, ["all", "hero", "howitworks", "calculator", "testimony", "faq"]) } router.events.on('routeChangeComplete', handleRouteChange) // 组件卸载时清理资源 return () => { router.events.off('routeChangeComplete', handleRouteChange) window._hw?.("destroy") // 清理window上的冲突变量 delete window._hw delete window['cherry-widget-script'] } }, [router]) return ( <Script id="cherry-widget-script" strategy="lazyOnload" src="**redacted**" onLoad={() => { window._hw("init", { debug: true, variables: { slug: 'hellovet-llc', name: 'HelloVet LLC', imageCategory: 'Veterinary', }, styles: { primaryColor: '#3b56a3', secondaryColor: '#3b56a310', fontFamily: 'open sans', headerFontFamily: 'raleway', }, }, ["all", "hero", "howitworks", "calculator", "testimony", "faq"]) }} onError={(e) => console.error('Cherry Widget加载失败:', e)} /> ) }
方案二:手动控制脚本加载(绕过Next.js Script组件缓存)
如果方案一仍有问题,可手动创建脚本标签,完全控制加载和初始化逻辑:
import { useEffect } from 'react' import { useRouter } from 'next/router' export default function CherryWidget() { const router = useRouter() useEffect(() => { let script = document.getElementById('cherry-widget-script') // 初始化_hw队列 window._hw = window._hw || function() { (window._hw.q = window._hw.q || []).push(arguments) } const initWidget = () => { // 清理旧实例和DOM元素 document.getElementById('all').innerHTML = '' window._hw?.("destroy") // 重新初始化 window._hw("init", { debug: true, variables: { slug: 'hellovet-llc', name: 'HelloVet LLC', imageCategory: 'Veterinary', }, styles: { primaryColor: '#3b56a3', secondaryColor: '#3b56a310', fontFamily: 'open sans', headerFontFamily: 'raleway', }, }, ["all", "hero", "howitworks", "calculator", "testimony", "faq"]) } if (!script) { // 动态创建脚本标签 script = document.createElement('script') script.id = 'cherry-widget-script' script.src = '**redacted**' script.async = true script.onload = initWidget script.onerror = (e) => console.error('Cherry Widget加载失败:', e) document.body.appendChild(script) } else { // 脚本已存在,直接初始化 initWidget() } // 路由切换时重新初始化 const handleRouteChange = () => initWidget() router.events.on('routeChangeComplete', handleRouteChange) // 卸载时清理 return () => { router.events.off('routeChangeComplete', handleRouteChange) window._hw?.("destroy") delete window._hw delete window['cherry-widget-script'] script?.remove() } }, [router]) return null }
关键注意事项
- 开启
debug: true,查看widget控制台日志,定位初始化失败的具体原因 - 确保挂载容器
<div id="all"></div>在组件初始化前已存在于DOM中 - 若widget未提供
destroy方法,可手动清空挂载容器的innerHTML来移除旧组件
内容的提问来源于stack exchange,提问作者Frank Pitkat
相关产品推荐
相关产品推荐

