vue2和vue3中父级属性改变,子组件props属性未改变,且不能触发监听的问题解决及响应式原理分析
场景:
- 调用自定义组件的时候,父级传给子组件的值可能是异步的,然后这也分两种情况
- 子组件的值只需要在初始化时获取,之后就不变了
- 子组件的值还会变化,在后面的操作中有异步请求,跟随父级拿到的数据变化
- 这时会有父级已经请求拿到数据了,但是子组件的值还没变,也没有触发回显,界面没有改变
解决:
vue2
- 虽然都是响应式的问题,但是解决思路可以不一样,但其实也可以一样
- 总体来说有几种解决方法:
- 用v-if控制组件在拿到数据之后再渲染,这很明显只能解决第一种情况,不适用于第二种,但是其实这个方法并不是从本质解决问题,而是投机取巧,但是还是记录一下吧:
<mycomp :data="data" v-if="is_data_got"></mycomp>mounted () { const that = this // getData 是封装好的接口调用 getData().then(res => { that.data= res.data that.is_data_got = true }) }- 网上很多博客说在子组件内用watch监听父级传过来的props,具体实现就不说了,比较简单。而且主要是因为这个方案也不是问题的根本,它甚至在我遇到的情况中并不管用,具有局限性。我遇到的这种情况的本质是父级在改变变量值的时候,并不是响应式的,所以不能触发子组件一起变化,甚至不能触发子组件的监听函数,尝试后发现,监听函数根本进不去。
- 本质:触发响应式
- vue响应式原理官方文档,强烈建议看一看
- 这里看下文档里的监测变化的注意事项这里,有几种特殊情况是非响应式的
- 对于对象变量,如果在声明时有指明的默认属性名,那么这些属性的变化是响应式的,但是如果是没有声明的额外属性,就是非响应式,官方例子:
var vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应式的 vm.b = 2 // `vm.b` 是非响应式的 - 对于数组,如果是用索引修改了数组的某一项,或修改数组长度,都是非响应式的,看官方例子:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的 - 我就是这种情况,而且对象和数据我都遇到了,解决办法:
- 对象型,可以在声明时直接初始化默认属性值,防止额外属性的出现。或者麻烦一点,重新对整个对象赋值
- 数组型,可以用Array.splice方法来替换指定位置的值,不用索引,如
arr.splice(index, 1, newValue) - 对象型和数组型,都可以用
this.$set(item, index/property, val)来强制响应,如:const obj = {} const arr = [1, 2] this.$set(obj, "name", "小明") // 这时obj.name = "小明" 就是响应式 this.$set(arr, 1, 3) // 这时arr[1] = 3 就是响应式
vue3
- vue3的组件是没有this的,所以很多this相关的用法都是改了的,其中this.$set是被摒弃的
- vue3中用ref和reactive做响应式声明,这两个属性的用法算是vue3的基础,需要熟练掌握
- ref一般用于基本类型声明
- 除了string, number, boolean等类型,也可以是对应的[],如
const var1 = ref<string[]>([]) - ref在js/ts中引用和修改的时候,都需要取value属性才行,如
var1.value.push('一个值'),这样才能正确响应 - ref在template中引用则不需要取value属性,直接用就行,如
<div v-for="item in var1">{{ item }}</div> - 除了声明响应式变量以外,ref还用于同vue2一样对HTMLElement的标记
<div ref="test">标记</div>,在vue2中调用ref标记的dom是用this.$refs.test,而如果是子组件,调用子组件的方法也可以直接this.$refs.test.func(),这一点上个人感觉还是vue2方便,在vue3,获取标记的dom比较简单,直接声明一个标记名相同的变量就行,如const test = ref(),这样就已经拿到test标记的div了,而如果要调用子组件的方法,则还需要在子组件中暴露给外部调用的方法,非setup写法就是export里面导出,而setup写法则是defineExpose({ func })
- 除了string, number, boolean等类型,也可以是对应的[],如
- reactive一般用于对象类型声明
- 如
const obj = reactive({ name: '对象' }),在js/ts和template中都直接用其对应属性
- 如
- ref一般用于基本类型声明
- 好啦,总结就是,只要变量变化时是响应式的,子组件就能一起变化,所以出现子组件不响应的情况时,首先看看父级的变量是不是响应式的

浙公网安备 33010602011771号