在vue中,有一个父组件和一个子组件,在父组件里有一个变量,在这个变量里有一个属性值和一个回调函数,并且这个变量是通过computed
直接return的,如下:
computed: {testObj() {return {name: '空标题',cb(option) {option.name = '标题更改了'}}}
}
在这个父组件中调用子组件并通过prop
的形式将这个对象传递给子组件,如下:
在testChild
这个子组件中,展示这个变量的name
值,并有一个事件会触发这个变量的回调函数,更改这个name
值,如下:
{{testObj.name}}
export default {name: 'TestChild',props:{testObj: {type: Object,default: ()=>({})}},mathods: {updateTest() {this.testObj.cb(this.testObj)console.log(this.testObj)}}
}
现在的问题是,在子组件触发更新事件后,通过console
打印可以看到testObj
中的name
值已经发生了改变,但是视图并没有更新
子组件视图之所以没有根据响应式实现更新跟computed
的实现原理有关,在vue中,可以通过data
、computed
、prop
、watch
的方式为当前实例中的变量添加上响应式(vue2通过defineProperty
方法,vue3通过proxy
,实现原理这里不赘述)。但是他们之间的实现方式有所不同,在vue源码中computed
是计算属性,也就意味着,computed
首先是监听参与计算的变量的变化,进而通知watcher
进行派发更新。如果参与计算的变量没有变化,而主动更改 computed 中的变量或者变量里的属性值,只能触发computed
的setter
方法(前提computed
是通过get
、set
设置的)。而在本示例下,父组件是返回一个完整的testObj
对象,在父组件中没有任何变量参与其中计算,这就使得我们在更改testObj
的属性值时,父组件认为这个testObj
并没有更新不需要触发watcher
进行派发更新。因此无论是在子组件还在在父组件,通过testObj.name
展示的值都无法进行更新。
解决这个问题的方法可以有多种
一,可以把这个变量放到data
中而不是computed
里
data() {return {testObj: {type: Object,default: ()=>({})}}
}
二、在计算属性中使用变量,通过更改这个变量实现computed更新
data() {return {isChange: false}
}
computed: {testObj() {return {name: isChange ? '标题更改了' : '空标题' ,cb(option) {this.isChange = true}}}
}
三、在子组件中通过变量赋值的方法,将该变量重新加上响应式
因为在vue中只要在data()中定义变量,vue就会自动为其添加响应式
{{testObj_ .name}}
props:[testObj: {type: Object,default: ()=>({})}
],
data() {return {testObj_: {}}
},
created() {
this.testObj_ = this.testObj
}
以上就是相关的优化方案。