You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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` .

核心原因

  1. Next.js客户端路由(Link跳转)为SPA模式,页面切换不会刷新整个页面,脚本加载后状态可能在路由切换时丢失
  2. 组件挂载时仅初始化一次window._hw,路由返回时组件重新挂载,但脚本已加载过,init逻辑未正确触发,且window上的冲突对象未清理
  3. 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
}

关键注意事项

  1. 开启debug: true,查看widget控制台日志,定位初始化失败的具体原因
  2. 确保挂载容器<div id="all"></div>在组件初始化前已存在于DOM中
  3. 若widget未提供destroy方法,可手动清空挂载容器的innerHTML来移除旧组件

内容的提问来源于stack exchange,提问作者Frank Pitkat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:20:02