news 2026/2/8 16:38:20

nextjs项目刷新报错window is not defined

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nextjs项目刷新报错window is not defined
on-recoverable-error.ts:30 Uncaught Error: Switched to client rendering because the server rendering errored: window is not defined at module evaluation (E:\source\m-yuying-n…ef3373f._.js:1864:1) at instantiateModule (E:\source\m-yuying-n…k]_runtime.js:715:9) at getOrInstantiateModuleFromParent (E:\source\m-yuying-n…]_runtime.js:738:12) at Context.esmImport [as i] (E:\source\m-yuying-n…]_runtime.js:228:20) at module evaluation (E:\source\m-yuying-n…3373f._.js:2048:139) at instantiateModule (E:\source\m-yuying-n…k]_runtime.js:715:9) at getOrInstantiateModuleFromParent (E:\source\m-yuying-n…]_runtime.js:738:12) at Context.esmImport [as i] (E:\source\m-yuying-n…]_runtime.js:228:20) at module evaluation (E:\source\m-yuying-n…3373f._.js:2764:141) at instantiateModule (E:\source\m-yuying-n…k]_runtime.js:715:9) at getOrInstantiateModuleFromParent (E:\source\m-yuying-n…]_runtime.js:738:12) at Context.esmImport [as i] (E:\source\m-yuying-n…]_runtime.js:228:20) at module evaluation (E:\source\m-yuying-n…3373f._.js:2794:138) at instantiateModule (E:\source\m-yuying-n…k]_runtime.js:715:9) at getOrInstantiateModuleFromParent (E:\source\m-yuying-n…]_runtime.js:738:12) at Context.commonJsRequire [as r] (E:\source\m-yuying-n…]_runtime.js:249:12) at Object.require (E:\source\m-yuying-n…ime.dev.js:40:31659) at globalThis.__next_require__ (E:\source\m-yuying-n…ime.dev.js:40:32458) at requireModule (E:\source\m-yuying-n…ime.dev.js:2:336682) at initializeModuleChunk (E:\source\m-yuying-n…ime.dev.js:2:375329) at getOutlinedModel (E:\source\m-yuying-n…ime.dev.js:2:386588) at E:\source\m-yuying-n…ime.dev.js:2:395211 at ResponseInstance._fromJSON (E:\source\m-yuying-n…ime.dev.js:2:395302) at JSON.parse (<anonymous>) at initializeModelChunk (E:\source\m-yuying-n…ime.dev.js:2:374523) at resolveModelChunk (E:\source\m-yuying-n…ime.dev.js:2:372866) at processFullStringRow (E:\source\m-yuying-n…ime.dev.js:2:427649) at E:\source\m-yuying-n…ime.dev.js:2:430328 at processBinaryChunk (E:\source\m-yuying-n…ime.dev.js:2:430412) at progress (E:\source\m-yuying-n…ime.dev.js:2:432161) <Suspense> Index @ Index.tsx:20 "use client" RootLayout @ layout.tsx:36

总结修复内容

我在 common.js 中修复了所有在模块级别或拦截器中使用浏览器 API 的地方:

修复的关键问题:

  1. 模块级别的window访问(第17-46行)

    • 原来:直接使用window !== window.parent
    • 修复:添加if (typeof window !== 'undefined')检查
  2. 请求拦截器中的浏览器 API(第48-89行)

    • 原来:直接使用localStoragewindow.platformdocument.location
    • 修复:在使用前添加typeof window !== 'undefined'typeof localStorage !== 'undefined'检查
  3. 响应拦截器中的浏览器 API(第128-194行)

    • 原来:直接使用localStorage.removeItem()window.location.reload()document.location.href
    • 修复:所有浏览器 API 调用都添加了类型检查

为什么会出现这个问题?

Next.js 使用服务器端渲染(SSR)。即使组件标记为'use client',在页面刷新时,Next.js 仍会在服务器端进行初始渲染。当api/common.js被导入时,其中的代码会在服务器端执行,但服务器端(Node.js)没有windowdocumentlocalStorage等浏览器专属的全局对象,因此会报错。

现在这些代码只会在浏览器环境中执行,服务器端渲染时会安全地跳过这些代码。

import axios from 'axios' import { message } from 'antd' // import { baseURL } from '@/utils/config' import qs from 'qs' import { showLoading, hideLoading } from '@/utils/tool' let isCanShow = true const service = axios.create() const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL || '' service.defaults.baseURL = baseURL let isInIframe = false let msgToChild = {} if (typeof window !== 'undefined') { if (window !== window.parent) { isInIframe = true //跨域 // if (window.parent) { // console.log(666, window?.parent) // if (window?.parent?.isCrx && window?.parent?.crxVersion) { // msgToChild = { // isCrx: window.parent.isCrx, // crxVersion: window.parent.crxVersion // } // } // } } window.addEventListener( 'message', function (event) { if (event.data) { try { msgToChild = JSON.parse(event.data) } catch (error) { msgToChild = {} } } }, false ) } service.interceptors.request.use( (config) => { //不同的平台提供不同的token和loginUserId if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') { if (config.isNotNeedToken !== true) { config.headers.authorization = localStorage.getItem('token') } if (window.platform === 'rn') { config.headers.platformos = localStorage.getItem('platformos') ? localStorage.getItem('platformos') : 'rn' config.headers.version = localStorage.getItem('appVersion') ? localStorage.getItem('appVersion') : '' } else if (isInIframe && msgToChild.isCrx) { config.headers.platformos = 'h5' config.headers.version = window.version config.headers.isCrx = msgToChild.isCrx config.headers.crxVersion = msgToChild.crxVersion } else { config.headers.platformos = 'h5' config.headers.version = window.version } } if (typeof document !== 'undefined') { config.headers.href = `${document.location.href}?platformos=${config.headers.platformos || 'h5'}&version=${config.headers.version || ''}` } config.timeout = 30 * 60 * 1000 // 请求接口时显示loading,接口响应后隐藏loading,如果有特殊情况不能满足需求的,例如同时请求了多个接口 // 且接口响应时间有比较大的差异,loading的显示隐藏状态不能友好的展示,可以直接在业务代码或api层,把 // isLoading设置为false,则不再使用拦截器控制loading的状态,自己在业务代码里手动控制loading的状态 if (config.isLoading !== false) { showLoading() } return config }, (err) => { return Promise.reject(err) } ) service.interceptors.response.use( (res) => { if (res.config.isLoading !== false) { setTimeout(() => { hideLoading() }, 500) } if (res.config.allResponse) { return res } else { //code是node接口的状态码,state是java接口的状态码 if (res.data.code === 200 || res.data.state === 1) { return res.data } else if ( res.data.code === 400 || res.data.code === 500 || res.data.state === 0 ) { let msg = '' msg = res.data.message if (res.data && res.data.data) { msg += res.data.data.error_msg ? `:${res.data.data.error_msg} ` : '' msg += res.data.data.error_code ? res.data.data.error_code : '' } if (res.config.isShowMessage !== false) { if (res.data?.data?.isLong) { message.open({ type: 'success', content: msg, duration: 30, }) } else { message.warning(msg) } } //Promise.reject(res) return res.data } else if (res.data.code === 403) { if (typeof localStorage !== 'undefined') { localStorage.removeItem('token') } if (isCanShow) { message.warning(res.data.message) isCanShow = false } setTimeout(() => { isCanShow = true }, 2000) if (typeof window !== 'undefined') { if (window.platform === 'rn') { message.warning('请重新登录') } else if (typeof document !== 'undefined') { if (document.location.href.includes('#/h5')) { window.reactRouter?.replace({ pathname: '/ai/login' }) } else if (document.location.href.includes('#/light')) { window.reactRouter?.replace({ pathname: '/light/login' }) } else if (document.location.href.includes('#/ai')) { window.reactRouter?.replace({ pathname: '/welcome/home' }) } else { //window.reactRouter.replace({ pathname: '/welcome/home' }) } } } return res.data } else if (res.data.code === 40001) { // if (isCanShow) { // message.warning(res.data.message) // isCanShow = false // } // setTimeout(() => { // isCanShow = true // }, 1000) // if (document.location.href.includes('#/h5')) { // window.reactRouter.replace({ pathname: '/h5/single/me/exchange' }) // } return res.data } else if (res.data.code === 40002) { if (isCanShow && typeof localStorage !== 'undefined') { isCanShow = false const reloadTime = Date.now() const historyReloadTime = localStorage.getItem('reloadTime') if (historyReloadTime) { if (reloadTime - historyReloadTime >= 60 * 1000) { message.warning(res.data.message) localStorage.setItem('reloadTime', reloadTime) if (typeof window !== 'undefined') { window.location.reload() } } } else { message.warning(res.data.message) localStorage.setItem('reloadTime', reloadTime) if (typeof window !== 'undefined') { window.location.reload() } } } setTimeout(() => { isCanShow = true }, 1000) return res.data } else if (res.data.code === 40003) { return res.data } else if (res.data.code === 40006) { return res.data } else { return Promise.reject(res) } } }, (err) => { hideLoading() let { response } = err if (typeof response === 'undefined') { return Promise.reject(err) } if (response) { let { status } = response if (status === 401) { } else { } } else { } return Promise.reject(err) } ) export const common = async (config) => { if (config.contentType === 'application/x-www-form-urlencoded') { config.headers = { 'content-type': 'application/x-www-form-urlencoded' } config.data = qs.stringify(config.data) } let res = await service(config) return res }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/5 15:26:22

AnimeGANv2创意玩法:制作动漫风格社交媒体故事

AnimeGANv2创意玩法&#xff1a;制作动漫风格社交媒体故事 1. 引言 1.1 社交媒体内容创新的AI驱动力 在短视频与社交动态主导信息传播的时代&#xff0c;个性化、视觉冲击力强的内容更容易获得关注。用户不再满足于简单的滤镜美化&#xff0c;而是追求更具艺术感和辨识度的视…

作者头像 李华
网站建设 2026/2/6 21:17:28

NomNom存档编辑器:开启《无人深空》游戏定制的无限可能

NomNom存档编辑器&#xff1a;开启《无人深空》游戏定制的无限可能 【免费下载链接】NomNom NomNom is the most complete savegame editor for NMS but also shows additional information around the data youre about to change. You can also easily look up each item ind…

作者头像 李华
网站建设 2026/2/5 6:14:42

一键启动.sh使用指南:VibeVoice-TTS脚本解析与避坑

一键启动.sh使用指南&#xff1a;VibeVoice-TTS脚本解析与避坑 1. 背景与应用场景 随着生成式AI技术的快速发展&#xff0c;文本转语音&#xff08;TTS&#xff09;系统已从单一音色、短句播报逐步演进为支持多角色、长篇内容生成的复杂框架。在播客制作、有声书合成、虚拟对…

作者头像 李华
网站建设 2026/2/7 17:50:24

HunyuanVideo-Foley餐厅用餐:餐具碰撞、点单、咀嚼声处理

HunyuanVideo-Foley餐厅用餐&#xff1a;餐具碰撞、点单、咀嚼声处理 1. 技术背景与应用场景 随着短视频和影视内容的爆发式增长&#xff0c;音效制作已成为提升视频沉浸感的关键环节。传统音效制作依赖专业 Foley 艺术家手动录制动作声音&#xff08;如脚步声、物品碰撞等&a…

作者头像 李华
网站建设 2026/2/8 2:05:17

AnimeGANv2实战:将历史照片转换成动漫风格的怀旧感

AnimeGANv2实战&#xff1a;将历史照片转换成动漫风格的怀旧感 1. 引言 1.1 业务场景描述 随着AI生成技术的普及&#xff0c;越来越多用户希望将普通照片、尤其是具有纪念意义的历史照片&#xff0c;转化为富有艺术感的二次元动漫风格。这类需求广泛存在于社交媒体头像定制、…

作者头像 李华
网站建设 2026/2/6 9:33:03

Tiny11Builder:重新定义Windows 11轻量化部署的终极方案

Tiny11Builder&#xff1a;重新定义Windows 11轻量化部署的终极方案 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 在Windows 11系统日益臃肿的今天&#xff0c;…

作者头像 李华