浅析如何保证vuex中的state动态添加属性的响应式及解决deep watch / computed监听vuex state对象属性变化不生效的问题
一、vuex 的 state 如何保证其响应式
vuex 的 store对象的 state 是响应式的,凡是预先在 state 中定义的状态,都会被加入到响应式系统中,当状态发生了变化,所有引用状态的 vue 文件的 template 都会随之发生变化,做到响应式的功能。
但是其前提是:提前定义好的会被加入到响应式系统中,那后面追加的属性或对象能不能加入到响应式系统中呢?
答案是:不能,但是,如果按照指定的方式来添加和删除属性,是可以做到响应式的。
比如现在我要添加一个新属性 height,用两种方式添加,如下:
1、方式1:此种方式能添加 state 中对象的属性,但无法加入到响应式系统中,即页面上不会显示出来,实际上确实存在了这个属性
mutations: {
addheight(state){
state.stus[0]['height'] = 168
}
}
2、方式2:此方式可以让属性加入到响应式系统中
mutations: {
addheight(state){
Vue.set(state.stus[1],'height',178) //参数一为对象,参数二为key,参数为value
}
}
3、方式3:用新对象给旧对象重新赋值(下面会介绍到)
4、说完添加属性,再说下删除吧,想要删除属性并加入响应式系统,如下方式:
mutations: {
delheight(state){
Vue.delete(state.stus[1],'height') //参数一为对象,参数二是key
}
}
5、总结:
(1)Vuex 的 store 对象中的 state 是响应式的,当 state 中的数据发生改变时,vue 组件会自动更新。
(2)但是它有一个前提条件,与我们之前了解的响应式前提是一致的,就是需要:提前在 state 中定义好所需的初始化属性。动态添加的属性是不能响应式的。
(3)如何保证动态添加的属性的响应式呢 - 有一下两种方式
方式一:使用 Vue.set(obj, key, value)
方式二:使用新对象给旧对象重新赋值
(4)在state删除已经存在的属性,使用 delete + 属性满足不了响应式,必须这样使用:Vue.delete(obj, 属性名)
二、解决 computed 监听 vuex 中 state 对象属性不生效的问题
首先我们需要了解一个前提基础:
computed 属性监听对象时候,若对象的引用地址未改变,那么computed将不会检测到。
比如object中的某个key对应的value发生了变化,computed检测不出来
// 原写法
export default {
namespaced: true,
state: {
info:{
other: {}
}
},
mutations: {
addInformation(state,info){
let data = Object.assign(state.info, info);
//此时 state.info 的值改变了,但是引用地址没变
state.shareInformation = data;
},
}
}
使用 Object.assign() 的目标对象仍然是 state.info ,所以虽然其值变了,但是引用地址是没变的,所以 computed 检测不到。
// 正确写法
addInformation(state,info){
//创建一个新的对象,将state.info,Info 对象复制到新对象中
let data = Object.assign({},state.info,info);
//将 state.info 指向新对象的引用地址
state.info = data;
},
使用 Object.assign() 的目标对象是个空对象,将 2 个源对象 state.info、info 的内容复制到新对象上 ,所以其值变了,引用地址也变了。这样 computed 才可以检测到。
三、vue 关于 deep watch 监听不到 vuex state 对象变化的的问题
简而言之,如果 vuex state 中是一个空对象 {},那么监听就会有问题,必须得有提前定义好的初始化属性才可以保证监听。那么如果无法确定动态添加属性的 key 是啥,怎么办呢?先给出解决方案:
// 简易拷贝
let _reply = JSON.parse(JSON.stringify(state.reply))
// 加入动态属性 orderId 是变化的
_reply[orderId] = data.orderId
// 更新,只能这样一波骚操作才能让computed和watch监听到。
state.reply = _reply
具体原因其实跟第二章一样,要保证其引用地址变了才行。