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
相关产品推荐
相关产品推荐

