news 2026/10/22 15:23:55

vue3中DataCloneError: Failed to execute ‘structuredClone‘ on ‘Window‘: #<Object> could not be cloned

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue3中DataCloneError: Failed to execute ‘structuredClone‘ on ‘Window‘: #<Object> could not be cloned
// 监视信息对话框状态,同步数据 watch(infoDialogVisible, (newValue) => { if (newValue) { // 对话框打开时,从store复制数据到本地副本 const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // Object.assign(localCapitalInfo, sourceData); // ❌浅拷贝:直接引用源对象,修改源数据可能影响目标,修改localCapitalInfo会影响sourceData,从而影响emptyCapitalInfo // Object.assign(localCapitalInfo, { ...sourceData }); // ✅浅拷贝:创建新对象,与源对象解耦,与源数据隔离,更安全,修改localCapitalInfo不会影响sourceData和emptyCapitalInfo // Object.assign(localCapitalInfo, JSON.parse(JSON.stringify(sourceData))); // ✅深拷贝:因为localCapitalInfo为reactive对象,不能直接赋值,如果是ref对象,可以 localCapitalInfo.value = JSON.parse(JSON.stringify(sourceData)); Object.assign(localCapitalInfo, structuredClone(sourceData)); // ✅深拷贝:现代浏览器原生方法 // 文件选择器实例清空资源,以便可以继续选择文件,不然会提示文件限制 baseFileSelectorRef.value?.cleanupResources(); } }); 报错:CapitalInfo.vue:415 Uncaught (in promise) DataCloneError: Failed to execute 'structuredClone' on 'Window': #<Object> could not be cloned. at CapitalInfo.vue:415:1

这个错误是因为structuredClone无法克隆某些特殊类型的对象。在你的场景中,sourceData很可能包含:

  1. Vue3 的响应式代理对象(reactive/proxy)

  2. 函数或方法

  3. Symbol 类型的属性

  4. 循环引用

  5. 特殊的 DOM 对象

解决方案

方案1:使用JSON.parse(JSON.stringify())(推荐)

typescript

watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 使用 JSON 方法进行深拷贝 Object.assign(localCapitalInfo, JSON.parse(JSON.stringify(sourceData))); baseFileSelectorRef.value?.cleanupResources(); } });

方案2:使用toRaw()剥离响应式代理

typescript

import { toRaw } from 'vue'; watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 先剥离响应式代理,再使用 structuredClone const rawData = sourceData === emptyCapitalInfo ? sourceData : toRaw(sourceData); Object.assign(localCapitalInfo, structuredClone(rawData)); baseFileSelectorRef.value?.cleanupResources(); } });

方案3:使用lodash.cloneDeep

typescript

import { cloneDeep } from 'lodash-es'; watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 使用 lodash 的深拷贝 Object.assign(localCapitalInfo, cloneDeep(sourceData)); baseFileSelectorRef.value?.cleanupResources(); } });

方案4:安全的structuredClone封装

typescript

function safeStructuredClone(obj: any): any { try { // 尝试使用 structuredClone return structuredClone(obj); } catch (error) { console.warn('structuredClone failed, falling back to JSON:', error); // 降级到 JSON 方法 return JSON.parse(JSON.stringify(obj)); } } watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 使用安全的深拷贝函数 Object.assign(localCapitalInfo, safeStructuredClone(sourceData)); baseFileSelectorRef.value?.cleanupResources(); } });

方案5:处理函数和特殊类型的深拷贝

typescript

function deepCloneForVue(obj: any): any { // 处理基本类型 if (obj === null || typeof obj !== 'object') { return obj; } // 处理数组 if (Array.isArray(obj)) { return obj.map(item => deepCloneForVue(item)); } // 处理普通对象(剥离 Vue 的响应式代理) const rawObj = toRaw(obj); const cloned = {}; for (const key in rawObj) { // 跳过 Vue 的内部属性 if (key.startsWith('__v_')) continue; const value = rawObj[key]; // 处理函数 if (typeof value === 'function') { // 可以保留函数引用,或者创建一个新的绑定 cloned[key] = value.bind(cloned); } // 处理 Date else if (value instanceof Date) { cloned[key] = new Date(value.getTime()); } // 递归处理对象 else if (value && typeof value === 'object') { cloned[key] = deepCloneForVue(value); } // 基本类型 else { cloned[key] = value; } } return cloned; } watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 使用自定义的 Vue 友好深拷贝 Object.assign(localCapitalInfo, deepCloneForVue(sourceData)); baseFileSelectorRef.value?.cleanupResources(); } });

针对你代码的最佳实践

考虑到你的数据类型CapitalInfo主要是基本类型和数组,推荐使用方案1或方案3:

推荐方案1:JSON 方法(最简单)

typescript

watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 简单可靠,适用于你的数据结构 Object.assign(localCapitalInfo, JSON.parse(JSON.stringify(sourceData))); baseFileSelectorRef.value?.cleanupResources(); } });

推荐方案3:使用 lodash(最专业)

typescript

import { cloneDeep } from 'lodash-es'; watch(infoDialogVisible, (newValue) => { if (newValue) { const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 专业可靠的深拷贝 Object.assign(localCapitalInfo, cloneDeep(sourceData)); baseFileSelectorRef.value?.cleanupResources(); } });

为什么structuredClone会失败?

在你的情况下,最可能的原因是:

  1. sourceData 是 Vue 的响应式代理对象

    • structuredClone无法克隆 Proxy 对象

    • 需要使用toRaw()先获取原始对象

  2. 对象中包含 Vue 的内部属性

    • 如__v_skip、__v_isRef等

    • 这些是 Vue 内部使用的,不可克隆

  3. 可能有循环引用

    • 虽然你的数据结构看起来没有,但 Vue 的响应式系统可能会创建

完整修复示例

typescript

import { watch } from 'vue'; import { cloneDeep } from 'lodash-es'; // 监视信息对话框状态,同步数据 watch(infoDialogVisible, (newValue) => { if (newValue) { // 对话框打开时,从store复制数据到本地副本 const sourceData = capitalInfoStore.currentSelectedCapitalInfo || emptyCapitalInfo; // ✅ 最佳方案:使用 lodash 的 cloneDeep Object.assign(localCapitalInfo, cloneDeep(sourceData)); // 或者使用 JSON 方法(如果确定没有函数、Date等特殊类型) // Object.assign(localCapitalInfo, JSON.parse(JSON.stringify(sourceData))); // 文件选择器实例清空资源,以便可以继续选择文件,不然会提示文件限制 baseFileSelectorRef.value?.cleanupResources(); } });

总结

方案优点缺点推荐度
JSON.parse(JSON.stringify())简单、无需依赖、处理大部分基本类型丢失函数、Date、undefined等⭐⭐⭐⭐
lodash.cloneDeep功能全面、可靠、保留更多类型需要引入第三方库⭐⭐⭐⭐⭐
toRaw + structuredClone浏览器原生、性能好需要处理Vue代理、可能仍有兼容问题⭐⭐
自定义深拷贝函数完全可控、可定制实现复杂、容易出错⭐⭐

根据你的错误信息和数据结构,推荐用lodash.cloneDeep或JSON.parse(JSON.stringify())。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 12:26:08

零基础M3U8视频下载避坑指南:从问题诊断到高效下载的完整方案

零基础M3U8视频下载避坑指南&#xff1a;从问题诊断到高效下载的完整方案 【免费下载链接】m3u8-downloader 一个M3U8 视频下载(M3U8 downloader)工具。跨平台: 提供windows、linux、mac三大平台可执行文件,方便直接使用。 项目地址: https://gitcode.com/gh_mirrors/m3u8d/…

作者头像 李华
网站建设 2026/10/19 12:26:11

PCBA叠层设计图解说明:四层板堆叠结构解析

以下是对您提供的博文《PCBA叠层设计图解说明:四层板堆叠结构解析》的 深度润色与专业重构版本 。本次优化严格遵循您的全部要求: ✅ 彻底去除AI痕迹(无模板化表达、无空洞术语堆砌、无机械连接词) ✅ 摒弃“引言/概述/总结”等程式化结构,全文以 真实工程师视角+项目…

作者头像 李华
网站建设 2026/10/19 10:10:31

DCT-Net人像卡通化镜像快速上手:10秒加载+WebUI一键转换保姆级步骤

DCT-Net人像卡通化镜像快速上手&#xff1a;10秒加载WebUI一键转换保姆级步骤 你是不是也试过在各种AI工具里反复上传照片、调整参数、等半天才出图&#xff0c;结果还发现卡通效果生硬、五官变形、背景糊成一团&#xff1f;别折腾了——这次我们直接用一个专为人像优化的GPU镜…

作者头像 李华
网站建设 2026/10/19 9:50:15

DDColor实测:一张黑白照如何变成彩色艺术品

DDColor实测&#xff1a;一张黑白照如何变成彩色艺术品 你有没有翻过家里的老相册&#xff1f;泛黄的纸页间&#xff0c;祖父穿着笔挺军装站在梧桐树下&#xff0c;祖母抱着襁褓中的父亲站在石库门门口——可所有画面都是灰白的。我们看得清他们的轮廓、表情、衣褶&#xff0c…

作者头像 李华