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

Next.js内部导航后原有JS脚本失效问题求助

解决Next.js内部导航后原有main.js脚本失效的问题

问题根源

Next.js的客户端路由采用单页应用模式,内部导航时不会刷新整个页面,仅动态更新DOM。你的main.js大概率是在首次页面加载时(比如DOMContentLoaded事件触发阶段)一次性绑定了DOM事件,但内部导航后新渲染的DOM元素未被重新绑定事件,导致脚本失效。直接重新加载脚本仅部分生效,是因为脚本加载完成时新DOM可能尚未完全渲染,事件绑定错过了时机。

解决方案

1. 优先采用事件委托(最稳定的长期方案)

修改main.js中的事件绑定逻辑,将直接绑定到元素的事件改为事件委托,绑定到document或body等稳定的父级节点。这样无论后续DOM如何更新,只要元素匹配选择器就能触发事件,无需重复加载脚本或绑定事件。

jQuery示例:

原写法:

$('.modal-trigger').click(function() {
  // 弹窗逻辑
});

改为事件委托:

$(document).on('click', '.modal-trigger', function() {
  // 弹窗逻辑
});

原生JS示例:

原写法:

document.querySelectorAll('.btn').forEach(btn => {
  btn.addEventListener('click', handleBtnClick);
});

改为事件委托:

document.addEventListener('click', function(e) {
  if (e.target.matches('.btn')) {
    handleBtnClick.call(e.target);
  }
});

2. 利用Next.js路由事件+脚本初始化方法

如果main.js暴露了全局初始化函数(比如window.initMain()),可在路由切换完成后,等待DOM渲染完毕再调用该方法,无需重复加载脚本。

import { useEffect } from 'react';
import { useRouter } from 'next/router';
import Script from 'next/script';

export default function RootLayout({ children }) {
  const router = useRouter();

  useEffect(() => {
    const initScript = () => {
      // 用requestIdleCallback确保DOM已完全渲染
      requestIdleCallback(() => {
        if (window.initMain) {
          window.initMain();
        }
      });
    };

    // 监听路由切换完成事件
    const handleRouteComplete = () => initScript();
    router.events.on('routeChangeComplete', handleRouteComplete);

    // 首次加载也执行初始化
    initScript();

    return () => {
      router.events.off('routeChangeComplete', handleRouteComplete);
    };
  }, [router]);

  return (
    <html lang="zh-CN">
      <body>
        {/* 仅加载一次脚本 */}
        <Script src="/Casino/assets/js/main.js" strategy="lazyOnload" />
        {children}
      </body>
    </html>
  );
}

3. 改进脚本重新加载逻辑(应急方案)

如果无法修改main.js,可优化现有脚本重新加载逻辑,确保脚本加载完成时DOM已完全渲染:

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function RootLayout({ children }) {
  const router = useRouter();

  useEffect(() => {
    // 移除页面中已存在的同名脚本
    const existingScript = document.querySelector('script[src="/Casino/assets/js/main.js"]');
    if (existingScript) {
      existingScript.remove();
    }

    const script = document.createElement('script');
    script.src = '/Casino/assets/js/main.js';
    script.async = true;

    // 脚本加载完成后,等待DOM稳定再执行初始化逻辑
    script.onload = () => {
      setTimeout(() => {
        // 模拟触发DOMContentLoaded事件,让脚本重新执行初始化
        document.dispatchEvent(new Event('DOMContentLoaded'));
      }, 100);
    };

    document.body.appendChild(script);

    return () => {
      script.onload = null;
      if (document.body.contains(script)) {
        script.remove();
      }
    };
  }, [router.asPath]);

  return (
    <html lang="zh-CN">
      <body>{children}</body>
    </html>
  );
}

4. 使用MutationObserver监听DOM变化

如果上述方案都不适用,可用MutationObserver监听DOM元素的添加,自动重新绑定事件:

import { useEffect, useRef } from 'react';
import { useRouter } from 'next/router';
import Script from 'next/script';

export default function RootLayout({ children }) {
  const router = useRouter();
  const observerRef = useRef(null);

  useEffect(() => {
    // 复写main.js中的事件绑定逻辑
    const rebindEvents = () => {
      if (window.jQuery) {
        // 解绑原有事件避免重复绑定
        window.jQuery('.modal-trigger').off('click');
        // 重新绑定事件
        window.jQuery('.modal-trigger').click(window.openModal);
        // 其他事件绑定同理
      }
    };

    // 创建观察者监听DOM变化
    observerRef.current = new MutationObserver((mutations) => {
      if (mutations.some(mut => mut.addedNodes.length > 0)) {
        rebindEvents();
      }
    });

    observerRef.current.observe(document.body, {
      childList: true,
      subtree: true
    });

    // 路由切换完成后手动触发一次绑定
    const handleRouteComplete = () => rebindEvents();
    router.events.on('routeChangeComplete', handleRouteComplete);

    return () => {
      router.events.off('routeChangeComplete', handleRouteComplete);
      if (observerRef.current) {
        observerRef.current.disconnect();
      }
    };
  }, [router]);

  return (
    <html lang="zh-CN">
      <body>
        <Script src="/Casino/assets/js/main.js" strategy="lazyOnload" />
        {children}
      </body>
    </html>
  );
}

内容的提问来源于stack exchange,提问作者Chris VanGroll

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:20:04