将Next.js项目部署至Vercel时遇ReferenceError: location未定义问题
解决Next.js部署Vercel时
ReferenceError: location is not defined的问题 你的问题核心是Next.js 13+ App Router的服务器组件与客户端组件区分导致的:你用的useRouter来自next/navigation,属于客户端钩子,而默认情况下组件是服务器组件,服务器端渲染时无法识别浏览器的location对象,也不能运行客户端钩子,所以触发了错误。
下面给你通用解决思路,以及针对你给出的三个组件的具体修改方案:
通用解决步骤
- 标记客户端组件:所有使用
useRouter、useSelector等客户端钩子的组件,必须在文件最顶部添加'use client';指令,强制组件在客户端渲染,这样就能正常访问浏览器API和客户端钩子。 - 延迟路由逻辑到客户端:如果组件初始化时就需要执行路由跳转(比如登录状态判断后跳转),要用
useEffect包裹逻辑,确保只在客户端渲染完成后执行(useEffect不会在服务器端运行)。
针对你的组件修改
1. LoginPage组件
原代码在服务器端会执行router.replace,导致报错。修改后:
'use client'; import { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useRouter } from 'next/navigation'; import Lottie from 'lottie-react'; import lottieLoad from '你的动画文件路径'; import Login from './Login'; const LoginPage = () => { const { authStatus } = useSelector((store: any) => store.shop); const router = useRouter(); // 用useEffect确保只在客户端执行跳转 useEffect(() => { if (authStatus) { router.replace("/"); } }, [authStatus, router]); if (authStatus) { return ( <> <div className="w-full h-screen flex justify-center items-center"> <Lottie className="w-[10rem] h-[10rem]" animationData={lottieLoad} loop={true} /> </div> </> ); } return ( <> <Login /> </> ); }; export default LoginPage;
2. WishlistBtn组件
点击事件本身是客户端触发的,但组件必须标记为客户端才能用useRouter和useSelector:
'use client'; import { useSelector } from 'react-redux'; import { useRouter } from 'next/navigation'; import appwriteServices from '你的AppWrite服务路径'; type Props = { product: any; // 建议替换为具体的产品类型定义 }; const WishlistBtn = ({ product }: Props) => { const { user, authStatus } = useSelector((store: any) => store.shop); const router = useRouter(); const handleAddWishlist = async () => { if (!authStatus) { router.replace("/login"); return; // 跳转后终止后续逻辑 } try { const existProduct = await appwriteServices.isProductInCart({ productId: product?._id, userId: user?.$id, }); if (existProduct) { // 这里可以添加商品已存在的提示逻辑 return; } await appwriteServices.createWishlist({ userId: user.$id, productName: product?.name, maxPrice: product?.maxPrice, minPrice: product?.minPrice, imageURL: product?.image?.asset._ref, stylist: product?.stylist, productId: product?._id, uniqueId: "", }); } catch (error) { console.error('添加心愿单失败:', error); // 可选:添加错误提示UI } }; // 补充按钮渲染逻辑(你原代码未提供) return ( <button onClick={handleAddWishlist} className="你的按钮样式"> 添加到心愿单 </button> ); }; export default WishlistBtn;
3. AddCartButton组件
同样标记为客户端组件,同时注意async函数里返回JSX无效,要用状态控制提示:
'use client'; import { useState } from 'react'; import { useSelector } from 'react-redux'; import { useRouter } from 'next/navigation'; import appwriteServices from '你的AppWrite服务路径'; import Message from './Message'; type Props = { product: any; // 建议替换为具体的产品类型定义 }; export default function AddCartButton({ product }: Props) { const [showMessage, setShowMessage] = useState(false); const router = useRouter(); const { user, authStatus } = useSelector((store: any) => store.shop); const handleAddToCart = async () => { if (!authStatus) { router.replace("/login"); return; } try { const productInCart = await appwriteServices.isProductInCart({ productId: product._id, userId: user?.$id, }); if (productInCart) { setShowMessage(true); return; } await appwriteServices.addToCart({ userId: user?.$id, imageURL: product?.image.asset._ref, productName: product?.name, minPrice: product?.minPrice, maxPrice: product?.maxPrice, stylist: product?.stylist, productId: product?._id, quantity: 1, }); router.push("/cart"); } catch (error) { console.error('添加购物车失败:', error); // 可选:添加错误提示 } }; return ( <> <button onClick={handleAddToCart} className="你的按钮样式"> 添加到购物车 </button> {showMessage && ( <Message text="抱歉,该商品已在购物车中" redirectRoute="/products" /> )} </> ); }
按上述方法修改后,重新部署到Vercel就能解决location is not defined的错误。所有使用客户端钩子的组件都要加'use client';,这是Next.js 13+ App Router的核心规则。
内容的提问来源于stack exchange,提问作者Rengkat
相关产品推荐
相关产品推荐

