watch监听事件未生效
在 Vue.js 中尝试使用 watch
监听 form.deptId
属性的变化,但在某些情况下监听器没有触发。
具体表现为:
- 在打开“添加”对话框时,
watch
监听器可以正常检测到deptId
的变化。 - 但是在打开“修改”对话框时,即使
form.deptId
被更新了,监听器却没有触发,导致后续逻辑没有执行。
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="deptOptions" :show-count="true"
placeholder="请选择归属部门" />
</el-form-item>
</el-col>
...
watch: {
// 监听deptId
'form.deptId': 'currDeptChange'
},
...
//新增/修改表单监听部门id改变事件
currDeptChange(val) {
console.log('在currDeptChange', val)
this.userOptions = [];
this.form.userIds = [];
listUser({ DeptId: val, status: "0" }).then(response => {
console.log("查询用户列表response:", response);
this.userOptions = response.data.dataSource;
});
// if (val) {
// this.queryStaff()
// }
},
这个问题的根源在于 Vue 的响应式系统的工作方式。Vue 的响应式系统可以自动检测数据的变化,但它对对象嵌套属性的直接赋值有一些限制。下面是详细解释:
1. Vue 的响应式系统原理
Vue 使用 Object.defineProperty
(Vue 3 中是基于 Proxy)来拦截对象的属性访问和赋值操作,从而实现响应式数据。当你在 Vue 的 data
中定义一个对象属性时,Vue 会把这个对象及其属性转换为响应式对象。之后,Vue 可以监测到这个属性的变化,并相应地更新 DOM 或触发 watch
监听器。
2. 问题的原因:直接赋值对嵌套属性的影响
在 data
中定义的对象本身是响应式的,但嵌套属性的直接赋值可能不会被 Vue 的响应式系统监测到。尤其是当你在 data
中定义了一个对象(例如 form
),但没有定义其中的嵌套属性(如 form.deptId
),Vue 不会自动监测到对 deptId
的直接赋值。
在你的情况中,deptId
属性可能在初始化 form
对象时没有被 Vue 监测到,因此在 handleUpdate
方法中直接赋值 this.form.deptId = response.data.belongsDeptId
时,Vue 的响应式系统不会检测到 deptId
的变化,所以 watch
监听器不会被触发。
3. 解决方法:使用 $set
Vue 提供了 $set
方法,用于确保某个属性变为响应式。$set
方法的工作原理是在对象上“定义”一个新的属性或重写现有属性,同时确保该属性会被 Vue 响应式系统检测到。
this.$set(this.form, 'deptId', response.data.belongsDeptId);
使用 $set
可以手动通知 Vue 这个属性的变化,使其重新进入响应式系统的监测范围,因此 Vue 可以触发 watch
监听器。$set
实际上是在对象上重新定义了这个属性,使得 Vue 可以检测到变化。
改后的代码
handleUpdate(row) {
console.log("row", row);
this.reset();
const id = row.id;
console.log("修改按钮操作id", id);
// getUserProfile(id).then(response => {
Get(id).then(response => {
this.form = response.data;
//变了这里
// 使用 $set 确保 deptId 的更新能触发 watch
this.$set(this.form, 'deptId', response.data.belongsDeptId);
GetEnableUser({ id: id, DistinguishingSigns: "2" }).then(res => {
console.log("resresresres", res);
this.userOptions = res.data.users;
//这里直接赋值
// this.form.userIds = res.data.userIds
this.$set(this.form, "userIds", res.data.userIds);
console.log("用户iduserIds", this.form);
});
this.open = true;
this.title = "修改值日信息";
});
},
总结
- 原因:在 Vue 中,直接赋值未在
data
中定义的嵌套属性不会被响应式系统监测到。 - 原理:Vue 的响应式系统基于
Object.defineProperty
或 Proxy。未在data
中初始化的嵌套属性不会自动变成响应式。 - 解决:使用
$set
手动添加或更新属性,使 Vue 的响应式系统可以检测到变化,并触发watch
监听器。