情景复现:
interface RuleForm {
addressEn: string;
addressZh: string;
companyEn: string;
companyZh: string;
contactEn: string;
contactZh: string;
designerNameEn: string;
designerNameZh: string;
itemQuantity: number;
phone: string;
mailbox: string;
projectNameList: Object[];
website: string;
}
const ruleFormRef = ref<FormInstance>();
let ruleForm = reactive<RuleForm>({
addressEn: '',
addressZh: '',
companyEn: '',
companyZh: '',
contactEn: '',
contactZh: '',
designerNameEn: '',
designerNameZh: '',
itemQuantity: 1,
phone: '',
projectNameList: [{ value: '' }],
website: '',
});
如上用reactive定义了一个数据变量,再通过接口赋值之后,发现页面没有给赋值回显
const initDetail = () => {
personalDetailApi(route.query.id).then(({ code, result }) => {
if (code === ERR_OK) {
const formattedArray = result.projectNameList.map(item => ({ value: item }));
ruleForm = {
...result,
projectNameList: formattedArray,
};
}
});
};
onBeforeMount(() => {
if (route.query.id) {
initDetail();
}
});
问题解决:
之前用的是结构赋值的方法 不能被reactive的响应式所监听 因此换为Object.assign的方法对问题进行重新赋值操作,问题可行解决。
const initDetail = () => {
personalDetailApi(route.query.id).then(({ code, result }) => {
if (code === ERR_OK) {
const formattedArray = result.projectNameList.map(item => ({ value: item }));
Object.assign(ruleForm, {
...result,
projectNameList: formattedArray,
});
}
});
};
解决原理:
Object.assign
方法之所以有效,是因为它直接修改了 reactive
对象的属性,而不是重新赋值整个对象。Vue 3 的 reactive
函数创建的响应式对象,内部使用了代理(Proxy)来追踪属性的变化。如果你直接重新赋值整个对象,代理关系会丢失,导致 Vue 无法追踪新的对象的变化。
使用 Object.assign
可以避免这个问题,因为它不会替换整个对象,而是逐个属性地更新对象的内容,从而保留了原有的代理关系和响应性。
以下是为什么这种方法有效的详细解释:
-
保持代理关系:
reactive
创建的对象是通过代理实现的,直接重新赋值会破坏这个代理关系。Object.assign
只是更新对象的属性,不会改变对象本身的引用,因此代理关系得以保留。 -
批量更新属性:
Object.assign
可以一次性更新多个属性,简化了代码,同时确保所有属性的变化都能被 Vue 追踪到。 -
响应性:Vue 通过代理对象的属性变化来实现响应性。只要属性的变化是在代理对象上进行的,Vue 就能检测到这些变化并更新视图。