news 2026/1/23 3:27:09

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/1/22 17:22:05

OpenPLC开源工业控制器:从零开始的完整实战指南

OpenPLC开源工业控制器&#xff1a;从零开始的完整实战指南 【免费下载链接】OpenPLC Software for the OpenPLC - an open source industrial controller 项目地址: https://gitcode.com/gh_mirrors/op/OpenPLC OpenPLC作为一款功能强大的开源工业控制器仿真平台&#…

作者头像 李华
网站建设 2026/1/20 14:36:29

打造梦幻岛屿:3步掌握Happy Island Designer的创意设计秘诀

打造梦幻岛屿&#xff1a;3步掌握Happy Island Designer的创意设计秘诀 【免费下载链接】HappyIslandDesigner "Happy Island Designer (Alpha)"&#xff0c;是一个在线工具&#xff0c;它允许用户设计和定制自己的岛屿。这个工具是受游戏《动物森友会》(Animal Cros…

作者头像 李华
网站建设 2026/1/14 9:21:59

Arknights-Mower终极指南:重新定义明日方舟基建管理效率

Arknights-Mower终极指南&#xff1a;重新定义明日方舟基建管理效率 【免费下载链接】arknights-mower 《明日方舟》长草助手 项目地址: https://gitcode.com/gh_mirrors/ar/arknights-mower 还在为《明日方舟》中重复繁琐的基建操作消耗大量时间吗&#xff1f;每天需要…

作者头像 李华
网站建设 2026/1/14 9:21:55

小白也能用!手把手教你使用Smart Doc Scanner处理发票和合同

小白也能用&#xff01;手把手教你使用Smart Doc Scanner处理发票和合同 1. 引言&#xff1a;为什么你需要一个智能文档扫描工具&#xff1f; 在日常办公或财务报销中&#xff0c;我们经常需要将纸质发票、合同、证件等文档数字化。传统方式是拍照后手动裁剪、拉直&#xff0…

作者头像 李华
网站建设 2026/1/18 18:39:55

HunyuanVideo-Foley电商应用:商品展示视频自动添加操作音效

HunyuanVideo-Foley电商应用&#xff1a;商品展示视频自动添加操作音效 1. 引言 1.1 业务场景描述 在电商平台中&#xff0c;商品展示视频是提升转化率的关键内容形式。高质量的视频不仅能清晰呈现产品细节&#xff0c;还能通过沉浸式体验增强用户购买意愿。然而&#xff0c…

作者头像 李华
网站建设 2026/1/14 9:21:32

2026 JPM医疗大会深度解读:AI浪潮如何重塑医药大健康未来

2026年1月&#xff0c;全球医疗健康领域最负盛名的年度盛会——摩根大通医疗健康大会&#xff08;J.P. Morgan Healthcare Conference, 简称JPM&#xff09;在旧金山如期举行。这座科技与创新之城再次汇聚了全球顶尖的制药巨头、生物科技新锐、医疗器械领导者、数字健康创新者以…

作者头像 李华