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

iOS端Expo React Native应用expo-splash-screen模块缺失问题求助

问题:Expo React Native iOS端expo-splash-screen模块报错及"main"未注册问题

错误现象

  • 导入expo-splash-screen时触发:
    ERROR  Error: Cannot find native module 'ExpoSplashScreen', js engine: hermes
    
  • 偶尔触发:
    Invariant Violation: "main" has not been registered. This can happen if: 
    * Metro (the local dev server) is run from the wrong folder. Check if Metro is 
    running, stop it and restart it in the current project. 
    * A module failed to load due to an error and "AppRegistry.registerComponent" 
    wasn't called., js engine: hermes 
    
  • 仅在iOS开发构建运行时出现,Android端正常。

用户代码实现

import React, { useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import {
  useFonts,
  Inter_400Regular,
  Inter_600SemiBold,
  Inter_700Bold,
  Inter_800ExtraBold,
} from '@expo-google-fonts/inter';
import Loading from './src/components/Loading';
import { QueryClient, QueryClientProvider } from 'react-query';
import {
  NavigationContainer,
  NavigationProp,
  useNavigation,
} from '@react-navigation/native';
import { DashboardRoutes } from './src/routes/DashboardRoutes';
import { AuthRoutes } from './src/routes/AuthRoutes';
import useAuthenticatedStore from '@stores/useAuthenticatedStore';
import { deleteToken, getToken, getUser } from '@storage/token';
import * as SplashScreen from 'expo-splash-screen';
import { AuthStackParamList } from 'src/types/types';
import { navigationRef } from './src/routes';
import Toast, {
  BaseToast,
  ErrorToast,
  SuccessToast,
} from 'react-native-toast-message';
import { jwtDecode } from 'jwt-decode';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

const queryClient = new QueryClient();

const toastConfig = {
  success: (props: any) => (
    <SuccessToast
      {...props}
      style={{ borderLeftColor: 'green' }}
      contentContainerStyle={{ paddingHorizontal: 15 }}
      text1Style={{
        fontSize: 15,
        fontWeight: '400',
      }}
    />
  ),
  error: (props: any) => (
    <ErrorToast
      {...props}
      text1Style={{
        fontSize: 17,
      }}
      text2Style={{
        fontSize: 15,
      }}
    />
  ),
};

const AppNavigation = () => {
  const { isAuthenticated, setUser, setIsAuthenticated, setProfilePictureUrl } =
    useAuthenticatedStore();
  console.log('isAuthenticated', isAuthenticated);

  let expiry: boolean | undefined;

  useEffect(() => {
    console.log('AppNavigation useEffect');
    async function prepare() {
      const token = await getToken();
      if (token) {
        expiry = await getUser();
      }
      console.log('expiry', expiry);
      try {
        if (token) {
          console.log('token before me api call', token);
          const url = //apicall here;
          const res = await fetch(url, {
            method: 'GET',
            headers: {
              Accept: 'application/json',
              Authorization: `Bearer ${token.trim()}`,
            },
          });
          const data = await res.json();
          console.log('data', data);
          setUser({
            email: data.email,
            name: data.name,
          });
          setProfilePictureUrl(data.profile_picture_url as string);
          setIsAuthenticated(true);
        }
      } catch (e) {
        if (token && expiry === true) {
          await deleteToken();
          setUser(null);
        }
      }
    }

    prepare();
  }, [isAuthenticated, expiry]);

  return null;
};

export default function App() {
  const [isReady, setIsReady] = useState(false);
  const [fontsLoaded] = useFonts({
    Inter_400Regular,
    Inter_600SemiBold,
    Inter_700Bold,
    Inter_800ExtraBold,
  });
  const { user } = useAuthenticatedStore();
  useEffect(() => {
    SplashScreen.preventAutoHideAsync().finally(() => setIsReady(true));
  }, []);

  useEffect(() => {
    if (isReady && fontsLoaded) {
      SplashScreen.hideAsync();
    }
  }, [isReady, fontsLoaded]);

  console.log('User', user);
  console.log('isReady', isReady);

  if (!isReady || !fontsLoaded) {
    return <Loading />;
  }

  return (
    <QueryClientProvider client={queryClient}>
      <NavigationContainer ref={navigationRef}>
        <GestureHandlerRootView style={{ flex: 1 }}>
          <AppNavigation />
          {user ? <DashboardRoutes /> : <AuthRoutes />}
          <Toast config={toastConfig} />
          <StatusBar style="dark" translucent />
        </GestureHandlerRootView>
      </NavigationContainer>
    </QueryClientProvider>
  );
}

解决方案

1. 修复expo-splash-screen模块缺失问题

  • 重新安装依赖:
    npx expo install expo-splash-screen
    
  • 检查app.json/app.config.js中splash screen配置是否完整,示例:
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    }
    

2. 清理iOS构建缓存并重建

  • 停止Metro服务,执行清理命令:
    npx expo prebuild --clean
    rm -rf ios/build
    
  • 清除Metro缓存并重启:
    npx expo start --clear
    
  • 重新构建iOS开发包:
    npx expo run:ios
    

3. 解决"main"未注册问题

  • 确保App.tsx默认导出组件无前置错误,避免因未捕获异常导致AppRegistry.registerComponent未执行
  • 优化splash异步操作的错误处理,避免异常中断组件加载:
    useEffect(() => {
      async function prepareSplash() {
        try {
          await SplashScreen.preventAutoHideAsync();
        } catch (e) {
          console.warn('Failed to prevent splash auto hide:', e);
        } finally {
          setIsReady(true);
        }
      }
      prepareSplash();
    }, []);
    

4. 检查Hermes引擎兼容性

  • 确认app.json中iOS的Hermes配置正确:
    "ios": {
      "hermesEnabled": true
    }
    
  • 若问题持续,可临时禁用Hermes测试是否为引擎兼容问题:
    "ios": {
      "hermesEnabled": false
    }
    

内容的提问来源于stack exchange,提问作者Waleed Taher

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:34:54