Vue 3 中不建议将大型 Blob 或二进制数据存入 reactive,因其触发深层代理、导致性能开销与内存泄漏;应改用 ref + markRaw 存储原始引用,并分离元信息与二进制数据。

在 Vue 3 中,不建议直接将大型 Blob 或二进制数据(如 ArrayBuffer、File、大型 Base64 字符串)存入 reactive 对象中——这会触发不必要的响应式追踪,造成性能开销甚至内存泄漏。
为什么 reactive 不适合存大型二进制数据
Vue 的响应式系统会对 reactive 中所有属性进行深层代理(Proxy),包括嵌套对象的每个字段。而 Blob 和 ArrayBuffer 是不可代理的原生类型,Vue 会尝试递归遍历其可枚举属性(如 size、type、lastModified 等),但更严重的是:
– 大型 ArrayBuffer 一旦被包裹进 reactive,可能被意外克隆或转为响应式副本;
– 浏览器对 Blob 的引用管理依赖于 GC,而响应式包装可能延长其生命周期;
– 每次读取或赋值都触发依赖收集,即使你从不监听这些字段。
推荐做法:用 ref 存储 + 手动控制响应性
对大型二进制数据,应使用 ref(非 shallowRef 或 customRef),并配合 markRaw 显式排除响应式处理:
- 用
ref包裹原始 Blob / ArrayBuffer,保持引用稳定性 - 立即用
markRaw标记,阻止 Vue 尝试代理它 - 仅在需要更新 UI(如预览图、文件名、大小)时,用独立的响应式字段驱动视图
示例:
立即学习“前端免费学习笔记(深入)”;
import { ref, markRaw } from 'vue'
const fileRef = ref(markRaw(file)) // file 是 input.files[0]
const fileInfo = ref({
name: file.name,
size: file.size,
type: file.type
})
这样,fileRef.value 始终是原始 Blob 引用,不会被 Proxy,也不会触发 getter/setter 开销。
上传前临时转为可序列化格式要谨慎
如果必须将二进制内容传给后端或做轻量处理(如裁剪、压缩),优先使用 URL.createObjectURL(blob) 生成临时 URL 用于预览,而不是把整个 blob 放进 reactive:
- 预览图片?用
createObjectURL+<img :src="previewUrl"> - 分片上传?用
blob.slice(start, end)按需切片,不全量加载到响应式对象中 - 需要转为 base64?只在必要时调用
readAsDataURL,且结果字符串存入普通 ref,而非嵌套在 reactive 里
替代方案:组合式逻辑分离数据与状态
把二进制数据视为“不可变资源”,将其生命周期交给外部逻辑管理(如自定义 Hook):
- 创建
useBinaryResource()Hook,内部用ref+markRaw管理 blob,暴露只读访问和显式销毁方法 - UI 层只订阅元信息(name、progress、status),由 Hook 内部触发更新
- 上传完成或组件卸载时,主动调用
URL.revokeObjectURL清理内存
这样既避免响应式污染,又保持了可维护性和内存安全性。


















