You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js+@emotion/native+react-native-web导航白屏闪烁问题排查

跨端应用Next.js导航白屏闪烁问题排查

我正在开发一款跨端应用,代码可在Next.js/react-native-web应用与React Native移动端应用间共享,但在Next.js应用中导航时会出现白屏闪烁现象。

我推测问题可能源于Emotion样式仅在客户端加载,因此尝试通过添加服务端Emotion缓存并使用缓存提供者来解决。

网上不少相关教程都比较陈旧,不确定如何适配现有代码,找到一篇相对较新的教程,核心思路是将Emotion样式注入页面头部,确保页面渲染时已包含CSS。

从配置来看我已正确设置,但白屏问题并未改善,可能存在疏漏。附上了相关配置文件及自定义导航组件示例,若缓存配置无误,怀疑问题是否与react-native-web的使用方式有关,恳请各位提供帮助。


_app.ts

import { ApolloProvider } from '@apollo/client'
import { SessionProvider } from 'next-auth/react'
import type { AppProps } from 'next/app'
import { client } from '../shared/graphql/apollo-client'
import { Toaster } from 'react-hot-toast'
import { CacheProvider } from '@emotion/react'
import createEmotionCache from '../utils/createEmotionCache'
import { EmotionCache } from '@emotion/cache'
import { Session } from '@/globalTypes/types'

const clientSideEmotionCache = createEmotionCache()

interface MyAppProps extends AppProps {
  emotionCache: EmotionCache
  session: Session
}

export default function App({
  Component,
  emotionCache = clientSideEmotionCache,
  pageProps: { session, ...pageProps }
}: MyAppProps) {
  console.log('emotionCache', emotionCache)

  return (
    <CacheProvider value={emotionCache}>
      <SessionProvider session={session}>
        <ApolloProvider client={client}>
          <Component {...pageProps} />
          <Toaster />
        </ApolloProvider>
      </SessionProvider>
    </CacheProvider>
  )
}

createComponentCache.ts

import createCache from '@emotion/cache'
const createEmotionCache = () => {
  return createCache({ key: 'css', prepend: true })
}
export default createEmotionCache

_document.ts

import Document, { Html, Head, Main, NextScript } from 'next/document'
import createEmotionCache from '../utils/createEmotionCache'
import createEmotionServer from '@emotion/server/create-instance'
import React from 'react'

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang='en'>
        <Head>
          <link
            rel='stylesheet'
            href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap'
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

MyDocument.getInitialProps = async (ctx) => {
  const originalRenderPage = ctx.renderPage

  const cache = createEmotionCache()
  const { extractCriticalToChunks } = createEmotionServer(cache)

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) =>
        function EnhanceApp(props) {
          // @ts-ignore
          return <App emotionCache={cache} {...props} />
        }
    })

  const initialProps = await Document.getInitialProps(ctx)

  const emotionStyles = extractCriticalToChunks(initialProps.html)

  const emotionStyleTags = emotionStyles.styles.map((style) => (
    <style
      data-emotion={`${style.key} ${style.ids.join(' ')}`}
      key={style.key}
      // eslint-disable-next-line react/no-danger
      dangerouslySetInnerHTML={{ __html: style.css }}
    />
  ))

  return {
    ...initialProps,

    styles: [
      ...React.Children.toArray(initialProps.styles),
      ...emotionStyleTags
    ]
  }
}

自定义导航组件示例

import { useSession } from 'next-auth/react'
import Link from 'next/link'
import { createStyled } from '@emotion/primitives-core'
import {
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native-web'

import { CustomButton } from '@/src/shared/ui/buttons'
import { Colors } from '@/src/shared/ui/constants'

interface Props {
  isLoggedIn: boolean
}

const LandingTopNav: React.FC<Props> = ({ isLoggedIn }) => {
  return (
    <Container>
      <LogoContainer>
        <Link href='/splash'>
          <LogoImage source={'/images/logo.png'} alt='Logo' />
        </Link>
      </LogoContainer>
      <RowContainer>
        <Link href='/pricing'>
          <CustomButtonWrapper>
            <CustomButton
              textColor={Colors.white}
              backgroundColor={Colors.black}
              borderColor={Colors.mediumGrey}
              hoverColor={Colors.darkGrey}
            >
              <NavButtonText>Pricing</NavButtonText>
            </CustomButton>
          </CustomButtonWrapper>
        </Link>

        {isLoggedIn ? (
          <Link href='/'>
            <CustomButtonWrapper>
              <CustomButton
                textColor={Colors.white}
                backgroundColor={Colors.brandPrimary}
                hoverColor={Colors.brandSecondary}
              >
                <NavButtonText>Go to App</NavButtonText>
              </CustomButton>
            </CustomButtonWrapper>
          </Link>
        ) : (
          <>
            <Link href='/auth/login'>
              <CustomButtonWrapper>
                <CustomButton
                  textColor={Colors.white}
                  backgroundColor={Colors.black}
                  borderColor={Colors.white}
                  hoverColor={Colors.darkGrey}
                >
                  <NavButtonText>Log in</NavButtonText>
                </CustomButton>
              </CustomButtonWrapper>
            </Link>
            <Link href='/auth/signup'>
              <CustomButtonWrapper>
                <CustomButton
                  textColor={Colors.white}
                  backgroundColor={Colors.brandPrimary}
                  hoverColor={Colors.brandSecondary}
                >
                  <NavButtonText>Get Started Free!</NavButtonText>
                </CustomButton>
              </CustomButtonWrapper>
            </Link>
          </>
        )}
      </RowContainer>
    </Container>
  )
}

const styled = createStyled(StyleSheet)

const Container = styled(View)`
  background-color: #1a202c;
  width: 100%;
  padding: 12px;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
`

const RowContainer = styled(View)`
  flex-direction: row;
  align-items: center;
`

const CustomButtonWrapper = styled(View)`
  margin: 8px;
`

const LogoContainer = styled(View)``

const LogoImage = styled(Image)`
  width: 60px;
  height: 60px;
`

const NavButtonText = styled(Text)`
  color: #fff;
  font-size: 16px;
`

export default LandingTopNav

内容的提问来源于stack exchange,提问作者Louis Sankey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 02:27:06