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

React Native(Expo)中使用jszip库时界面冻结问题求助

解决Expo React Native中ZIP压缩导致UI冻结的问题

问题代码

import JSZip from "jszip";

const createArchive = async() => {
    // Some code
    const zip = new JSZip();
    const data = getExampleData();
    zip.file("example.json", data);
    const zipContent = await zip.generateAsync(
        { type: "base64" },
        (metadata) => {
          setCreationProgress(metadata.percent); // Set a progress value for ui
          console.log("Value:", metadata.percent);
        }
      );// When this function is executed the ui freezes
    // Some code
}

问题描述

在基于Expo的React Native项目中,上述代码能正常完成ZIP压缩功能,但执行zip.generateAsync()时,应用界面会完全冻结直到压缩完成。如果处理大文件,耗时会很长,期间界面无法更新进度、也无法交互,用户既看不到压缩进度,也没法取消操作。

试过用expo-task-manager和expo-background-fetch把任务移到后台线程,但没成功;也考虑过更换压缩库,但不清楚哪些适配Expo,求可行的解决方案。


可行解决方案

方案1:用react-native-threads在独立线程执行压缩(适配Expo)

这个库能把CPU密集型任务放到独立线程,避免阻塞UI线程,且支持Expo环境。

  • 安装依赖:
    npx expo install react-native-threads
    
  • 创建独立线程文件(如zip-thread.js):
    import JSZip from "jszip";
    
    self.onmessage = async (message) => {
      const { data } = message;
      const zip = new JSZip();
      zip.file("example.json", data);
      
      try {
        const zipContent = await zip.generateAsync(
          { type: "base64" },
          (metadata) => {
            self.postMessage({ type: "progress", percent: metadata.percent });
          }
        );
        self.postMessage({ type: "complete", content: zipContent });
      } catch (error) {
        self.postMessage({ type: "error", error });
      }
    };
    
  • 主组件中调用线程:
    import { Thread } from 'react-native-threads';
    
    const createArchive = async () => {
      const thread = new Thread('./zip-thread.js');
      const data = getExampleData();
      
      thread.postMessage(data);
      
      thread.onmessage = (message) => {
        switch(message.type) {
          case "progress":
            setCreationProgress(message.percent);
            break;
          case "complete":
            const zipContent = message.content;
            // 处理压缩完成后的逻辑
            thread.terminate(); // 结束线程释放资源
            break;
          case "error":
            // 处理错误逻辑
            thread.terminate();
            break;
        }
      };
    
      // 提供取消压缩的方法
      window.cancelZipCompression = () => {
        thread.terminate();
        setCreationProgress(0);
      };
    };
    

方案2:更换为Expo兼容的原生压缩库

原生库的压缩效率远高于JSZip,且不会阻塞UI线程,但部分库需要EAS构建(无法在Expo Go中直接使用)。

选项A:react-native-zip-archive(需EAS构建)
  • 安装依赖:
    npx expo install react-native-zip-archive
    
  • 使用示例(结合expo-file-system):
    import { zip } from 'react-native-zip-archive';
    import * as FileSystem from 'expo-file-system';
    
    const createArchive = async () => {
      // 创建临时目录存放待压缩文件
      const tempDir = `${FileSystem.cacheDirectory}temp_zip/`;
      await FileSystem.makeDirectoryAsync(tempDir, { intermediates: true });
      
      // 写入待压缩文件
      const data = getExampleData();
      await FileSystem.writeAsStringAsync(`${tempDir}example.json`, data);
      
      // 目标压缩文件路径
      const targetZipPath = `${FileSystem.cacheDirectory}archive.zip`;
    
      // 进度回调
      const progressCallback = (progress) => {
        setCreationProgress(progress.percent);
      };
    
      try {
        // 执行压缩
        await zip(tempDir, targetZipPath, {}, progressCallback);
        // 读取为base64格式
        const zipContent = await FileSystem.readAsStringAsync(targetZipPath, { encoding: 'base64' });
        // 处理压缩完成后的逻辑
      } catch (error) {
        // 处理错误
      } finally {
        // 清理临时文件
        await FileSystem.deleteAsync(tempDir, { idempotent: true });
        await FileSystem.deleteAsync(targetZipPath, { idempotent: true });
      }
    };
    
选项B:expo-ssziparchive(iOS优先,需EAS构建)

基于iOS原生SSZipArchive的封装,压缩效率高,用法和react-native-zip-archive类似,适合iOS为主的项目。

方案3:优化JSZip的执行(缓解阻塞)

如果不想更换库或使用线程,可通过调整JSZip的参数和进度更新逻辑,减少UI阻塞,但大文件下效果有限:

const createArchive = async () => {
  const zip = new JSZip();
  const data = getExampleData();
  zip.file("example.json", data);
  
  let lastProgress = 0;
  const updateProgress = (metadata) => {
    // 降低进度更新频率,避免UI频繁重绘
    if (metadata.percent - lastProgress > 2) {
      lastProgress = metadata.percent;
      // 把进度更新放到事件循环末尾,给UI线程留时间处理交互
      setTimeout(() => setCreationProgress(lastProgress), 0);
    }
  };
  
  // 启用流式处理,减少内存占用同时降低阻塞
  const zipContent = await zip.generateAsync(
    { type: "base64", streamFiles: true },
    updateProgress
  );
};

内容的提问来源于stack exchange,提问作者Oleh Chernysh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:59:58