如何利用 structuredClone 拷贝具备 Blob 或 File 原始属性的复杂表单对象

作者:袖梨 2026-06-13
structuredClone()可安全深拷贝含Blob/File/FormData等对象的表单数据,但需确保浏览器支持(Chrome 98+/Firefox 97+/Safari 15.4+/Node.js 17.0+),对不支持环境需降级处理;克隆要求Blob/File为原生实例,元数据应独立存储;克隆后Blob引用有效,但object URL需重新生成;FormData需先转为键值对再克隆。

structuredClone() 可以安全深拷贝包含 Blob、File、FormData、Map、Set 等可转移对象的表单数据,但需注意浏览器兼容性与特殊限制。

确认环境支持并处理不支持场景

structuredClone() 在现代浏览器(Chrome 98+、Firefox 97+、Safari 15.4+)中可用,Node.js 17.0+ 也支持。若需兼容旧环境,应降级使用 Blob.slice() 手动重建 Blob,或借助 FileReader + ArrayBuffer 序列化再反序列化。

  • 检查支持:if (typeof structuredClone === 'function') { ... }
  • 不支持时,对含 Blob/File 的对象,避免直接 JSON.stringify(会丢失二进制内容)
  • 可封装 fallback 函数:对 Blob 创建新实例(new Blob([blob], {type: blob.type})),对 File 需额外传入 name 和 lastModified(File 构造函数支持)

正确构造含 Blob/File 的表单对象

structuredClone 要求原始对象中的 Blob/File 必须是“可结构化克隆”的值——即原生实例,而非被 Proxy 包裹、或经自定义属性扩展的对象。例如,直接从 input[type="file"].files[0] 获取的 File 实例可被克隆;但若手动添加了 .customId 等属性,克隆后该属性会丢失(structuredClone 不保留非自有可枚举属性)。

  • 确保 Blob/File 是原生实例,未被 Object.assign 或解构修改原型
  • 如需携带元数据(如上传 ID、标签),建议用独立字段保存,而非挂载在 Blob/File 实例上
  • 示例安全结构:
    { file: input.files[0], metadata: { id: 'xxx', tag: 'avatar' } }

克隆后保持文件引用有效性

structuredClone 会创建新的 Blob/File 实例,其内容与原实例共享底层字节(通过内部引用计数),因此克隆后的 Blob 仍可正常读取、上传或转为 URL(URL.createObjectURL())。但注意:克隆不会复制已生成的 object URL,需对新 Blob 单独调用 URL.createObjectURL()。

  • 克隆前后 Blob.size、.type、.name(File)均保持一致
  • 克隆后的 File 实例仍是 instanceof File,可直接用于 FormData.append()
  • 若原 Blob 来自 fetch 响应,克隆后仍可流式读取(.stream() 有效)

与 FormData 配合使用的注意事项

structuredClone 本身不支持直接克隆 FormData(它不是可结构化类型),但可先将其转换为普通对象或数组,再克隆。推荐方式是遍历 FormData.entries(),提取键值对,对 Blob/File 值直接保留,其他值按需处理:

  • const formObj = Array.from(formData.entries()).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
  • 再用 structuredClone(formObj) 得到深拷贝对象
  • 如需还原为 FormData,遍历克隆后的对象,对每个值调用 new FormData().append(k, v)
  • 注意:FormData 中同名多值(如多个 checkbox)会被 entries() 按顺序展开,还原时需确保 append 顺序一致

相关文章

精彩推荐