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

React.js中生成大体积PPT调用writeFile导致UI短暂卡顿及下载时机异常问题

问题根因

两个异常表现本质都是pptxgen.js的writeFile方法的执行逻辑导致的:

  • 虽然writeFile返回Promise,但它把所有幻灯片、资源打包成pptx二进制流的核心计算逻辑是同步运行在浏览器主线程的。120MB文件的打包计算量会直接阻塞主线程,所有UI渲染、状态更新都会被挂起,所以你会看到页面出现1秒左右的无响应卡顿。
  • 加载动画和下载不同步是因为你把setIsGenerating(false)写在了writeFile的then回调里:writeFile先执行完同步打包逻辑(这段时间主线程被阻塞,你之前调用的setIsGenerating(true)还没来得及被React渲染成加载动画就被卡住了),打包完成后立刻触发下载,同时才会执行then回调更新状态,等主线程空闲后React才会执行重渲染关闭加载动画,所以你会感知到下载比动画消失早1秒。
    另外你说120MB体积不大但性能差,是因为浏览器主线程JS执行是单线程的,只要同步计算超过50ms用户就会感知到卡顿,打包120MB文件的计算量远超过这个阈值,和文件本身大小没有绝对关系。

解决方案

1. 快速修复(改少量代码即可见效)

首先调整状态更新时机,先保证加载动画渲染出来再执行计算逻辑,同时把无用的map调用换成forEach减少内存开销,另外不用等writeFile返回promise再关闭加载动画,打包完成触发下载后就可以直接关闭:

const createPPT = async () => {
    setIsGenerating(true)
    // 加setTimeout把后续逻辑放到下一个宏任务,等待React先渲染出加载动画再执行计算
    setTimeout(async () => {
        let cart = JSON.parse(localStorage.getItem('cart')) || []
        let pres = new pptxgen();
        let newCart = JSON.parse(JSON.stringify(cart))
        const imgList = []
        await Promise.all(newCart.map(async (d) => {
            if (d.img !== "") {
                let result = await axios.get(d.img, {
                    responseType: 'arraybuffer'
                })
                // 优化:不用转base64,直接传二进制给pptxgen,减少转码开销
                imgList.push(result.data)
            }
        }))
        // 原来的map没有用到返回值,换成forEach更合理
        newCart.forEach((d, i) => {
            if (d.img !== "") {
                d.img = imgList[i]
            }
        })
        newCart.forEach(d => {
            let slide = pres.addSlide()
            d.img && slide.addImage({ data: d.img, w: "100%", sizing: { type: "crop" } });
            slide.addText(d.title, { x: '0', y: '0', w: '100%', h: '20%', align: 'center', valign: 'middle', color: "ffffff", });
            slide.background = { color: "#000000" };
            d.content.forEach(text => {
                let slide_2 = pres.addSlide();
                d.img && slide_2.addImage({
                    data: d.img,
                    w: "100%",
                    sizing: {
                        type: "crop"
                    }
                });
                slide_2.addText(text, { x: '0', y: '0', w: '100%', h: '20%', align: 'center', valign: 'middle', color: "ffffff", })
                slide_2.background = { color: "#000000" };
            })
        })

        pres.writeFile({ fileName: "Sample Presentation.pptx" })
        // writeFile同步执行完成就已经触发下载,直接关闭加载动画即可
        setIsGenerating(false)
    }, 0)
}

2. 彻底解决卡顿

如果按上面的方法修改后还是有明显卡顿,可以把PPT生成、打包的全部逻辑放到Web Worker中执行,完全不占用主线程资源,就不会出现UI阻塞的问题:

  • 新建独立的worker文件,引入pptxgen库,把图片拉取、幻灯片生成、打包逻辑都放在worker内部实现
  • 主线程点击按钮后先开启加载动画,把cart数据发送给worker
  • worker执行完所有逻辑后给主线程发送打包完成的消息,主线程收到消息后直接关闭加载动画即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:06:01