Expo 字体加载成功但不渲染的解决方案

2次阅读

本文详解 Expo 中 useFonts 返回 isLoaded 为 true 却仍无法渲染自定义字体的根本原因——未正确触发 SplashScreen 隐藏逻辑,并提供可立即生效的修复方案与最佳实践。

本文详解 expo 中 `usefonts` 返回 `isloaded` 为 `true` 却仍无法渲染自定义字体的根本原因——未正确触发 splashscreen 隐藏逻辑,并提供可立即生效的修复方案与最佳实践。

在 Expo 应用中,使用 expo-font 的 useFonts Hook 加载自定义字体是一个常见操作。但开发者常遇到一个看似矛盾的现象:isLoaded 返回 true,控制台无报错,路径确认无误,字体文件也已正确注册,然而 <Text style={{fontFamily: ‘Typographica’}}>Hello</Text> 依然回退到系统默认字体(如 San Francisco 或 Roboto),直到手动注释 / 取消注释代码才“意外”生效。

根本原因在于:useFonts 仅负责异步加载并返回加载状态,它本身不阻塞渲染,也不自动触发 UI 重绘或 SplashScreen 隐藏。而你当前代码中关键的 handleOnLayout 是一个被 useCallback 包裹的函数,却从未被调用!

const handleOnLayout = useCallback(async () => {if (isLoaded) {await SplashScreen.hideAsync();   } }, [isLoaded]); // ❌ 此函数被定义,但从未执行 —— 它只是个“待命的函数”,不是副作用逻辑 

useCallback 的作用是缓存函数引用以避免不必要的子组件重渲染,但它不会自动运行。因此,即使 isLoaded === true,SplashScreen.hideAsync() 也不会执行,导致 Splash Screen 持续遮盖内容,且后续文本因字体尚未被“正式激活”(需配合布局完成与重绘)而无法正确应用。

正确做法:将字体加载完成后的副作用逻辑移至 useEffect 中,并依赖 isLoaded 触发:

import React, {useState, useEffect} from 'react'; import {useNavigation} from '@react-navigation/native'; import {StyleSheet, Text, View, TouchableOpacity, ScrollView} from 'react-native'; import {useFonts} from 'expo-font'; import * as SplashScreen from 'expo-splash-screen';  // 防止 Splash Screen 自动隐藏(必须在组件外或顶层调用)SplashScreen.preventAutoHideAsync();  const HomeScreen = () => {const [isLoaded] = useFonts({'Typographica': require('nocram/assets/fonts/Typographica.ttf'),     'NeulisAltRegular': require('nocram/assets/fonts/NeulisAlt-Regular.ttf'),     'NeulisAltExtraBold': require('nocram/assets/fonts/NeulisAlt-ExtraBold.ttf'),   });    // ✅ 使用 useEffect 响应 isLoaded 变化,主动执行隐藏逻辑   useEffect(() => {     if (isLoaded) {SplashScreen.hideAsync().catch(console.error);     }   }, [isLoaded]);    // ⚠️ 注意:务必确保组件在 isLoaded 为 true 前不渲染依赖自定义字体的 UI   // 否则可能出现闪烁或回退字体。推荐结合条件渲染:if (!isLoaded) {return null; // 或显示 loading placeholder}    return (<View style={styles.container}>       <Text style={[styles.title, { fontFamily: 'Typographica'}]}>Welcome to Nocram</Text>       <Text style={[styles.body, { fontFamily: 'NeulisAltRegular'}]}>Practice makes perfect.</Text>     </View>   ); };  const styles = StyleSheet.create({container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff'},   title: {fontSize: 28, fontWeight: 'normal'},   body: {fontSize: 16, marginTop: 12}, });  export default HomeScreen;

? 关键注意事项:

  • SplashScreen.preventAutoHideAsync() 必须在组件挂载前调用 (通常放在 App.js 顶部或 _layout.tsx 中),否则可能因时机问题失效;
  • 避免在 isLoaded === false 时渲染 fontFamily 样式 ,否则 React Native 会尝试应用未就绪字体,导致不可预测的回退行为;
  • 若使用 expo-router,建议在 app/_layout.tsx 中统一处理字体加载与 Splash 控制,提升首屏体验一致性;
  • 在生产环境(尤其是 EAS Build),请确保字体文件已包含在 assetBundlePatterns 中(app.json 或 app.config.js),否则 iOS/Android 打包后字体路径会失效。

遵循以上模式,即可彻底解决“字体已加载却无法渲染”的典型陷阱——本质不是字体没加载,而是 UI 生命周期未与加载状态正确同步。

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-25发表,共计2489字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources