Vue3中如何保留Props初始值?表单组件状态追踪需求
Vue3中存储未知结构Props初始值的最佳实践
针对你开发可复用表单组件时需要存储Props初始值做变更对比的场景,以下是具体方案和分析:
关于toRaw()的适用性:你的用法是安全的
Vue文档不建议持久持有toRaw()获取的原对象,核心原因是原对象可能被外部(比如父组件)修改,导致你持有的值意外变化。但你当前的写法是structuredClone(toRaw(props.modelValue))——你并没有持有原对象本身,而是立刻对原对象做了深克隆,得到了一个完全独立的副本。这个副本和原对象彻底断开了引用关系,后续原对象的任何修改都不会影响它,完全符合存储初始值的需求,所以这个用法是安全的,不需要担心文档的警告。
推荐方案:toRaw() + structuredClone()组合
这个组合是当前场景下最优的选择,理由如下:
toRaw()能精准去除Vue3对Props对象添加的Proxy包装,拿到原始的纯JS对象,避免克隆过程中把响应式代理也复制进去structuredClone()原生支持深克隆,能处理几乎所有JS数据类型(包括循环引用、Date、Map、Set等),比JSON序列化方法更可靠,也不需要额外引入第三方库
具体实现代码:
import { toRaw, ref, watch } from 'vue'; import _ from 'lodash'; // 可选,用于深度对比 export default { props: { modelValue: { type: Object, required: true } }, setup(props) { // 存储初始值的副本 const originalFormValue = ref(null); // 初始化+监听父组件更新modelValue时同步更新初始值 watch(() => props.modelValue, (newVal) => { // 先取原对象,再深克隆 originalFormValue.value = structuredClone(toRaw(newVal)); }, { immediate: true, deep: true }); // 判断表单是否变更的方法 const isFormChanged = () => { // 对比时取当前值的原对象,避免Proxy干扰 const currentRawValue = toRaw(props.modelValue); // 用深度对比方法判断是否一致 return !_.isEqual(currentRawValue, originalFormValue.value); }; return { isFormChanged }; } };
替代方案:无toRaw()的实现(可选)
如果你对toRaw()有顾虑,也可以直接对props.modelValue使用structuredClone()——因为structuredClone()会自动忽略Proxy的代理层,直接序列化原始数据。不过这种写法在某些复杂嵌套响应式对象场景下可能存在潜在风险,所以更推荐搭配toRaw()使用。
避坑提示
- 不要直接存储
props.modelValue本身:它是响应式的Proxy对象,后续表单修改会直接改变这个值,无法保留初始状态 - 不要用JSON序列化:遇到循环引用、Date、Map等类型时会失败或丢失数据
- 如果父组件会动态更新
modelValue,一定要用watch监听并同步更新初始值副本,否则初始值会和最新传入的值不一致
内容的提问来源于stack exchange,提问作者Norah
相关产品推荐
相关产品推荐

