Jarallax在Next.js/TypeScript中报错:Cannot read properties of undefined (reading 'userAgent')
问题
基于Next.js/TypeScript复刻使用Jarallax的模板时,编译组件触发模块错误,错误信息如下:
Server Error Error: Cannot read properties of undefined (reading 'userAgent') This error happened while generating the page. Any console logs will be displayed in the terminal window. Call Stack eval node_modules\jarallax\dist\jarallax.esm.js (131:1) (rsc)/./node_modules/jarallax/dist/jarallax.esm.js file:///C:/wamp64/www/_site/.next/server/vendor-chunks/jarallax.js (30:1) Next.js eval /./src/app/components/About/About.tsx (rsc)/./src/app/components/About/About.tsx file:///C:/wamp64/www/_site/.next/server/app/page.js (195:1) Next.js eval /./src/app/page.tsx (rsc)/./src/app/page.tsx file:///C:/wamp64/www/_site/.next/server/app/page.js (363:1) Next.js
相同代码在纯客户端环境可正常运行,但在Next.js SSR环境中报错,不清楚如何正确引入Jarallax以及是否需要自定义组件。
原组件核心代码:
import React from 'react' import { jarallax, jarallaxElement } from "jarallax"; const About = () => { React.useEffect(() => { jarallaxElement(); jarallax(document.querySelectorAll("[data-jarallax-element]")); }, []) return ( <section className="elementor-section ..." id="about"> {/* 省略页面DOM结构 */} </section> ) } export default About
解决方案
错误根源
Jarallax初始化时会读取window.navigator.userAgent判断浏览器环境,但Next.js默认SSR模式下,服务端没有window对象,导致读取userAgent时触发undefined错误。
修复步骤
- 动态导入Jarallax,限制在客户端加载
使用React的动态导入函数,确保Jarallax仅在客户端环境初始化:
import React, { useEffect, useRef } from 'react'; const About = () => { const sectionRef = useRef<HTMLDivElement>(null); useEffect(() => { const initJarallax = async () => { // 动态导入Jarallax及元素扩展 const { jarallax, jarallaxElement } = await import('jarallax'); jarallaxElement(); // 通过ref精准获取元素,避免全局DOM查询 if (sectionRef.current) { jarallax(sectionRef.current.querySelectorAll("[data-jarallax-element]")); } }; initJarallax(); // 清理副作用:页面卸载时销毁Jarallax实例 return () => { import('jarallax').then(({ jarallax }) => { if (sectionRef.current) { sectionRef.current.querySelectorAll("[data-jarallax-element]").forEach(el => { jarallax(el, 'destroy'); }); } }); }; }, []); return ( <section ref={sectionRef} className="elementor-section ..." id="about"> {/* 保留原页面DOM结构 */} </section> ) }; export default About;
- 封装可复用Jarallax组件(可选)
如果多个页面需要使用Jarallax,可封装通用组件简化调用:
// components/JarallaxElement.tsx import React, { useEffect, useRef, PropsWithChildren } from 'react'; interface JarallaxElementProps { speedX?: number; speedY?: number; className?: string; } const JarallaxElement: React.FC<PropsWithChildren<JarallaxElementProps>> = ({ speedX = 0, speedY = 0, className, children }) => { const elementRef = useRef<HTMLDivElement>(null); useEffect(() => { const initJarallax = async () => { const { jarallax, jarallaxElement } = await import('jarallax'); jarallaxElement(); if (elementRef.current) { jarallax(elementRef.current, { type: 'element', speed: [speedX, speedY] }); } }; initJarallax(); return () => { import('jarallax').then(({ jarallax }) => { if (elementRef.current) { jarallax(elementRef.current, 'destroy'); } }); }; }, [speedX, speedY]); return ( <div ref={elementRef} className={className} data-jarallax-element={`${speedX} ${speedY}`} > {children} </div> ); }; export default JarallaxElement;
调用示例:
<JarallaxElement speedX={0} speedY={-30} className="elementor-element ..."> {/* 图片或内容 */} </JarallaxElement>
- 直接禁用组件SSR(极端场景)
若上述方法无效,可通过Next.js的dynamic函数强制组件仅在客户端渲染:
// page.tsx import dynamic from 'next/dynamic'; const About = dynamic(() => import('./components/About/About'), { ssr: false }); export default function Home() { return <About />; }
内容的提问来源于stack exchange,提问作者The Old County
相关产品推荐
相关产品推荐

