关键在于主动控制内存生命周期,尤其在微信x5内核和千元安卓机上:压缩图片再上传、及时释放canvas和blob url、简化dom结构、上传后立即清空file引用。

关键不是等系统回收,而是主动控制内存生命周期——尤其在微信 X5 内核和千元安卓机上,GC 触发迟滞、堆内存上限低(常仅 64–128MB),靠浏览器自动清理极易导致闪退或页面刷新。
压缩图片再上传,避免 Base64 阻塞内存
van-uploader 或原生 input[type=file] 获取文件后,不要直接用 FileReader.readAsDataURL()。该方法会将整张图转为 Base64 字符串,在红米 Note 系列等设备上,一张未压缩的 4000×3000 照片可能生成 30MB+ 的字符串对象,瞬间占满 JS 堆。
- 改用 canvas 绘制缩放 + toBlob() 生成压缩后的 Blob,目标尺寸建议 ≤1200px 宽、质量 0.7–0.8
- 对超过 2MB 的原始文件,强制触发压缩;小于 500KB 可跳过,减少无谓计算
- 压缩完成后立即释放 canvas、context、临时 URL:URL.revokeObjectURL(tempUrl)
及时销毁临时 Blob URL 和引用链
UniApp 的 tempFilePath 或 H5 中 createObjectURL 生成的 blob:http://... 地址,本质是内存中 Blob 的弱引用入口。若不手动清理,即使 DOM 节点已 remove,该 Blob 仍驻留内存,阻碍 GC。
- 每次预览或上传完成,立刻调用 URL.revokeObjectURL(url),哪怕只是“试一下”
- 避免将 blob URL 赋值给全局变量或长期存在的对象属性(如 window.cacheImg)
- 在组件 onUnmounted / beforeDestroy 中统一 revoke 所有已创建的 URL
简化 DOM 结构,防止深层嵌套干扰 GC 可达性
实测发现:van-uploader 默认渲染含 5 层以上 div 嵌套,配合内联事件绑定(如 @click.native)时,X5 内核在 GC 标记阶段易因栈深超限而跳过子树,造成“节点已删但内存不降”。
- 用 scoped slot 替代默认结构,把 uploader 外层 wrapper 控制在 ≤3 层
- 移除所有 onclick="xxx()" 类内联写法,统一用 addEventListener 绑定,并在卸载时 removeEventListener
- 避免在 uploader 内部插入大量 v-if/v-for 动态节点,改用显式 show/hide 控制显示
上传后立即清除文件对象引用
file 对象本身虽小,但它持有底层 Blob 引用。若上传成功后仍保留在 this.file 或 vuex state 中,且该对象被闭包捕获(如 upload.then(() => this.file = null) 未执行),整个 Blob 就无法释放。
- 上传请求发起后,立刻置空本地 file 变量:this.file = null
- 使用 try/finally 确保无论成功失败都清空:finally { this.file = null }
- 服务端返回成功响应后,不再保留 file.name、file.type 等冗余信息,改用后端返回的 fileId 或 url











