Expo React Native安卓端运行报错:JS编译失败
Expo React Native加载大型JSON安卓端报错:Invalid UTF-8 continuation byte
问题描述
在Expo React Native应用中加载大型JSON文件时,网页端运行正常,但安卓端出现编译错误:
Compiling JS failed: 624709:3354959:Invalid UTF-8 continuation byte Buffer size 160752371 starts with: ...
已尝试操作
- 验证JSON文件有效性
- 检查文件中是否存在无编码字符
- 尝试在useEffect钩子中逐个加载JSON文件
安卓模拟器错误截图

组件代码
import { useEffect, useState } from 'react'; import { Pressable, ScrollView, StyleSheet, useColorScheme, } from 'react-native'; import HTMLView from 'react-native-htmlview'; import { useRecoilState } from 'recoil'; import baghawyJSON from '@/assets/tafaseer/baghawy.json'; import earabJSON from '@/assets/tafaseer/earab.json'; import katheerJSON from '@/assets/tafaseer/katheer.json'; import maanyJSON from '@/assets/tafaseer/maany.json'; import muyassarJSON from '@/assets/tafaseer/muyassar.json'; import qortobyJSON from '@/assets/tafaseer/qortoby.json'; import saadyJSON from '@/assets/tafaseer/saady.json'; import tabaryJSON from '@/assets/tafaseer/tabary.json'; import tanweerJSON from '@/assets/tafaseer/tanweer.json'; import waseetJSON from '@/assets/tafaseer/waseet.json'; import { Colors } from '@/constants'; import { tafseerTab } from '@/recoil/atoms'; import { TafseerAya, TafseerTabs } from '@/types'; import { ThemedText } from './ThemedText'; import { ThemedView } from './ThemedView'; import surahs from '../assets/quran-metadata/mushaf-elmadina-warsh-azrak/surah.json'; const tabLabels: Record<TafseerTabs, string> = { katheer: 'ابن كثير', maany: 'معاني القرآن', earab: 'إعراب القرآن', baghawy: 'البغوي', muyassar: 'الميسر', qortoby: 'القرطبي', tabary: 'الطبري', saady: 'السعدي', tanweer: 'التحرير و التنوير', waseet: 'الوسيط', }; type Props = { aya: number; surah: number; opacity: number; }; export default function Tafseer({ aya, surah, opacity }: Props) { const colorScheme = useColorScheme(); const tintColor = Colors[colorScheme ?? 'light'].tint; const textColor = Colors[colorScheme ?? 'light'].text; const backgroundColor = Colors[colorScheme ?? 'light'].background; const [surahName, setSurahName] = useState<string>(''); const [selectedTabValue, setSelectedTab] = useRecoilState<TafseerTabs>(tafseerTab); const [tafseerData, setTafseerData] = useState<TafseerAya[] | null>(null); const renderTafseerContent = (tafseer: TafseerAya[] | null): JSX.Element => { const ayaTafseer = tafseer?.find((t) => t.aya === aya && t.sura === surah); let tafseerText = 'لا يوجد تفسير.'; if (!ayaTafseer?.text || ayaTafseer?.text === '<p></p>') { tafseerText = '<p>لا يوجد تفسير.</p>'; } else { tafseerText = ayaTafseer?.text; } return ( <ThemedView style={styles.tafseerContent}> <HTMLView value={tafseerText} stylesheet={{ p: { color: textColor, fontFamily: 'Amiri_400Regular' }, }} /> </ThemedView> ); }; useEffect(() => { setSurahName(surahs[surah - 1]?.name ?? ''); }, [surah]); useEffect(() => { switch (selectedTabValue) { case 'baghawy': setTafseerData(baghawyJSON as TafseerAya[]); break; case 'earab': setTafseerData(earabJSON as TafseerAya[]); break; case 'katheer': setTafseerData(katheerJSON as TafseerAya[]); break; case 'maany': setTafseerData(maanyJSON as TafseerAya[]); break; case 'muyassar': setTafseerData(muyassarJSON as TafseerAya[]); break; case 'qortoby': setTafseerData(qortobyJSON as TafseerAya[]); break; case 'saady': setTafseerData(saadyJSON as TafseerAya[]); break; case 'tabary': setTafseerData(tabaryJSON as TafseerAya[]); break; case 'tanweer': setTafseerData(tanweerJSON as TafseerAya[]); break; case 'waseet': setTafseerData(waseetJSON as TafseerAya[]); break; default: setTafseerData(katheerJSON as TafseerAya[]); } }, [selectedTabValue]); return ( <ScrollView contentContainerStyle={[styles.scrollView, { backgroundColor, opacity }]} > <ThemedText style={styles.title}> {surahName} - الآية {aya} </ThemedText> <ThemedView style={styles.tabs}> {Object.keys(tabLabels).map((key) => { const tabKey = key as TafseerTabs; return ( <Pressable key={tabKey} style={[ styles.tabButton, selectedTabValue === tabKey && styles.activeTab, selectedTabValue === tabKey && { borderColor: tintColor }, ]} onPress={() => setSelectedTab(tabKey)} > <ThemedText>{tabLabels[tabKey]}</ThemedText> </Pressable> ); })} </ThemedView> {renderTafseerContent(tafseerData)} </ScrollView> ); } const styles = StyleSheet.create({ scrollView: { padding: 20, }, title: { fontSize: 18, marginBottom: 10, }, tabs: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 2, justifyContent: 'flex-start', }, tabButton: { paddingVertical: 10, paddingHorizontal: 15, }, activeTab: { borderBottomWidth: 2, }, tafseerContent: { marginBottom: 20, marginTop: 20, }, });
解决方案
1. 动态加载JSON(避免编译时打包)
当前代码编译时直接导入所有大型JSON,导致安卓打包工具处理UTF-8编码出错。改用动态加载方式:
useEffect(() => { const loadTafseerData = async () => { let jsonModule; try { switch (selectedTabValue) { case 'baghawy': jsonModule = await import('@/assets/tafaseer/baghawy.json'); break; case 'earab': jsonModule = await import('@/assets/tafaseer/earab.json'); break; case 'katheer': jsonModule = await import('@/assets/tafaseer/katheer.json'); break; case 'maany': jsonModule = await import('@/assets/tafaseer/maany.json'); break; case 'muyassar': jsonModule = await import('@/assets/tafaseer/muyassar.json'); break; case 'qortoby': jsonModule = await import('@/assets/tafaseer/qortoby.json'); break; case 'saady': jsonModule = await import('@/assets/tafaseer/saady.json'); break; case 'tabary': jsonModule = await import('@/assets/tafaseer/tabary.json'); break; case 'tanweer': jsonModule = await import('@/assets/tafaseer/tanweer.json'); break; case 'waseet': jsonModule = await import('@/assets/tafaseer/waseet.json'); break; default: jsonModule = await import('@/assets/tafaseer/katheer.json'); } setTafseerData(jsonModule.default as TafseerAya[]); } catch (error) { console.error('加载JSON失败:', error); } }; loadTafseerData(); }, [selectedTabValue]);
2. 重新编码JSON文件
用VS Code等编辑器打开JSON文件,选择文件 -> 保存编码 -> UTF-8重新保存,确保文件无隐藏的非UTF-8字符。
3. 拆分大型JSON文件
将单个大型JSON按章节拆分为多个小文件,减少单次加载的文件体积,降低编码处理压力。
4. 调整Metro配置
修改metro.config.js,优化JSON文件的编译处理:
const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); config.resolver.sourceExts.push('json'); config.transformer.minifierConfig = { keep_classnames: true, keep_fnames: true, mangle: false, }; module.exports = config;
内容的提问来源于stack exchange,提问作者Adel Benyahia
相关产品推荐
相关产品推荐

