FileReader加载事件与DOM渲染不同步,如何触发文本框渲染完成事件?
问题根因
- FileReader的
onload回调触发时,文件读取、JSON解析、格式化字符串的步骤确实已经全部完成,给textarea赋值的代码本身执行耗时极低 - 浏览器的DOM渲染属于事件循环中的宏任务,优先级低于当前正在执行的同步JS代码。你在赋值后直接执行的
alert("finished loading")属于同步代码,会在DOM渲染更新前先执行,因此会先弹出完成提示,再阻塞5秒执行textarea的内容渲染,看起来就像完成事件提前触发。
修复方案
实现DOM渲染完成后再触发完成提示
利用requestAnimationFrame的执行时机特性,嵌套两层即可保证回调在当前DOM更新渲染完成后执行,修改fr.onload逻辑如下:
fr.onload = function(e) { console.log(e); var result = JSON.parse(e.target.result); var formatted = JSON.stringify(result, null, 2); document.getElementById('importTextArea').value = formatted; // 两层requestAnimationFrame保证等待DOM渲染完成 requestAnimationFrame(() => { requestAnimationFrame(() => { console.log("finished loading"); alert("finished loading"); }) }) }
大文本渲染性能优化(解决卡顿、Chrome崩溃问题)
千万级字符直接写入原生textarea会带来极大的渲染压力,甚至触发内存上限崩溃,可通过以下方式优化:
- 关闭textarea的冗余特性:在现有
spellcheck="false"基础上,补充autocorrect="off" autocapitalize="off",禁用浏览器自带的文本校验、自动更正逻辑,减少渲染时的额外计算开销 - 采用虚拟滚动方案:如果不需要全量编辑所有文本,不要直接把全量内容写入textarea,用虚拟滚动组件仅渲染可视区域的内容,内存占用可降低99%以上,完全消除卡顿
- 分批次写入内容:如果必须使用原生textarea,可将大字符串拆分为多个小片段,用
setTimeout分批次插入,避免单次任务长时间阻塞主线程,防止页面假死 - 减少DOM写入次数:3700万字符的文本体积接近几十MB,多次写入DOM会反复触发渲染,有跨文本处理需求时尽量先在内存中完成字符串拼接处理,最后仅做一次DOM写入。
内容的提问来源于stack exchange,提问作者Pie
相关产品推荐
相关产品推荐

