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 的地方:
修复的关键问题:
-
模块级别的
window访问(第17-46行)- 原来:直接使用
window !== window.parent - 修复:添加
if (typeof window !== 'undefined')检查
- 原来:直接使用
-
请求拦截器中的浏览器 API(第48-89行)
- 原来:直接使用
localStorage、window.platform、document.location - 修复:在使用前添加
typeof window !== 'undefined'和typeof localStorage !== 'undefined'检查
- 原来:直接使用
-
响应拦截器中的浏览器 API(第128-194行)
- 原来:直接使用
localStorage.removeItem()、window.location.reload()、document.location.href - 修复:所有浏览器 API 调用都添加了类型检查
- 原来:直接使用
为什么会出现这个问题?
Next.js 使用服务器端渲染(SSR)。即使组件标记为 'use client',在页面刷新时,Next.js 仍会在服务器端进行初始渲染。当 api/common.js 被导入时,其中的代码会在服务器端执行,但服务器端(Node.js)没有 window、document、localStorage 等浏览器专属的全局对象,因此会报错。
现在这些代码只会在浏览器环境中执行,服务器端渲染时会安全地跳过这些代码。
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
}
更多推荐
所有评论(0)