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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:08:10