Next.js通过next/link跳转时next/script加载的第三方脚本失效问题
问题根因
beforeInteractive策略的脚本仅在应用首次硬加载时注入执行一次,你把jQuery、slick这类全局依赖放在首页组件里加载,客户端路由从其他页面跳回首页时,脚本不会重复执行,甚至可能出现依赖加载顺序错乱的问题。- 你写在
functions.js里的slick初始化逻辑,默认绑定在页面首次DOM ready事件上,只会在硬刷新后触发一次。客户端路由切换时,轮播对应的DOM节点是React重新渲染生成的新节点,但初始化逻辑没有重新执行,slick实例没有绑定到新DOM上,功能自然失效。
修复方案
1. 全局依赖统一移到_document.js加载
把所有全站通用的第三方基础脚本从首页组件移到pages/_document.js中加载,保证所有路由场景下依赖都能提前按顺序加载完成,注意jQuery必须放在所有依赖它的插件前面:
// pages/_document.js import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return ( <Html lang="zh-CN"> <Head /> <body> <Main /> <Script src="assets/js/jquery.min.js" strategy="beforeInteractive" /> <Script src="assets/js/bootstrap.min.js" strategy="beforeInteractive" /> <Script src="assets/js/slick.js" strategy="beforeInteractive" /> <Script src="assets/js/jquery.fancybox.min.js" strategy="beforeInteractive" /> <Script src="assets/js/wow.js" strategy="beforeInteractive" /> <NextScript /> </body> </Html> ) }
2. 组件内单独初始化slick
不要靠全局functions.js统一初始化插件,在轮播组件挂载完成后再执行slick初始化,组件卸载时销毁实例,从根源适配React的生命周期:
// components/MainSlider.js import { useEffect, useRef } from 'react' export default function MainSlider({ images }) { const sliderNode = useRef(null) useEffect(() => { // DOM渲染完成后初始化slick,配置和原项目保持一致 if (window.$ && sliderNode.current) { window.$(sliderNode.current).slick({ infinite: true, slidesToShow: 1, autoplay: true, // 其余原有slick配置项 }) } return () => { // 组件卸载时销毁实例,避免内存泄漏、重复绑定问题 if ( window.$ && sliderNode.current && window.$(sliderNode.current).hasClass('slick-initialized') ) { window.$(sliderNode.current).slick('unslick') } } }, [images]) return ( <div ref={sliderNode} className="slider-container"> {images.map((item, idx) => ( <div key={idx} className="slide-item"> <img src={item.src} alt={item.alt} /> </div> ))} </div> ) }
其他说明
functions.js里其余的插件初始化逻辑(WOW、fancybox、bootstrap事件绑定等),都要拆到对应业务组件的useEffect中执行,不要依赖全局一次性的DOM ready事件。- 如果暂时不想拆分原
functions.js逻辑,可以通过Next.js路由事件监听,每次路由切换完成后重新执行全局初始化,但这种方式维护性差,容易出现重复绑定问题,仅适合临时兼容:
// 临时兼容方案,不推荐长期使用 import { useRouter } from 'next/router' import Script from 'next/script' import { useEffect } from 'react' export default function HomePage() { const router = useRouter() const initPlugins = () => { // 执行原functions.js中的所有初始化逻辑 if (window.$) { window.$('.slick-slider').not('.slick-initialized').slick({/* 原有配置 */}) new window.WOW().init() window.$('[data-fancybox]').fancybox({/* 原有配置 */}) } } useEffect(() => { router.events.on('routeChangeComplete', initPlugins) return () => router.events.off('routeChangeComplete', initPlugins) }, [router.events]) return ( <> <Script src="assets/js/functions.js" strategy="afterInteractive" onReady={initPlugins} /> {/* 页面其余组件 */} </> ) }
内容的提问来源于stack exchange,提问作者Ali Jand
相关产品推荐
相关产品推荐

