Vue3怎么正确监听props?能监听到数组对象内部变化吗?有没有更省性能的写法?
很多刚转Vue3或者入门不久的开发者,在处理父子组件数据通信时,都会遇到props监听的问题:要么直接写个普通watch没用上,要么数组对象内部改了没反应,要么为了省事开了deep:true却发现页面卡,今天咱们就把Vue3监听props的所有情况、写法、优缺点、避坑指南,甚至连Composition API和Options API的对比都掰扯清楚,看完你肯定不会再在这上面踩坑了。
先搞懂:Vue3 props本身的特点和触发变化的条件
在聊监听之前,先把props的“脾气”摸透——为什么有时候不写监听也能拿到新值?为什么写了监听有时候还没触发?这都是基础,但基础最容易被忽略。
Vue3不管是Options API还是Composition API,默认的props都是单向数据流,而且是响应式的浅传递(这里先不说defineProps在setup顶层自动解包ref的情况,后面单独讲坑)。
- 单向数据流:意思是父组件传过来的props,子组件不能直接修改——哪怕你改了,Vue3也会在控制台给你报错,这是为了避免数据流混乱,不然多个子组件都改同一个父组件数据,你根本不知道哪里出的问题。
- 响应式浅传递:举个例子,父组件传的是
{ name: '张三', age: 18 }这个对象,或者[1,2,3]这个数组——如果父组件直接替换掉整个对象/数组的引用(比如user.value = { name: '李四', age: 20 },或者list.value = [4,5,6]),那子组件的props肯定会自动更新,哪怕你没写监听,模板里也能直接拿到新值渲染,但如果父组件只改了对象的属性值(比如user.value.age = 21),或者只改了数组的元素、长度(比如list.value[0] = 7,list.value.push(8))——那这个时候,浅传递的响应式能不能触发呢? 哦,这里要分Options API和Composition API的setup情况:- Options API(包括script setup之外的传统setup):不管是对象属性还是数组元素的修改,只要父组件的源数据是用
data()、reactive()定义的响应式数据,子组件的props就能自动在模板里更新,但如果要在子组件的逻辑(比如methods、computed之外的watch回调)里拿到这个变化,就得写监听,而且普通watch还不一定够。 - script setup:这里有个小坑,但也是官方给的“糖”——如果父组件传的是用ref包裹的原始值类型(比如number、string、boolean),那子组件
const props = defineProps(['userName'])之后,在setup顶层可以直接用props.userName(ref被自动解包成原始值了);如果是用ref包裹的对象/数组,或者直接用reactive定义的对象/数组,那父组件传过来的还是ref或者reactive的引用,子组件在setup里用的时候不用解包(比如props.user.age),模板里也能自动更新,但同样的,浅改内部值要逻辑监听的话,得特殊处理。
- Options API(包括script setup之外的传统setup):不管是对象属性还是数组元素的修改,只要父组件的源数据是用
正式开讲:Vue3监听props的N种写法
现在咱们从基础到进阶,逐个讲写法,每个写法都带场景、代码、优缺点、避坑点,还有Composition API和Options API的对比(现在主流用script setup,也就是Composition API的语法糖,所以主要讲这个,Options API只提关键区别)。
场景1:监听“原始值类型”的props,或者“引用值类型但父组件会整体替换引用”的props
这个是最简单的情况,用普通的watch就可以搞定。
代码示例(script setup)
假设父组件是这样传的:
<!-- 父组件 Parent.vue -->
<template>
<div>
<h2>我是父组件</h2>
<input v-model="userName" placeholder="改原始值类型的名字看看" />
<input v-model.number="userAge" placeholder="只改对象的年龄浅监听没用" />
<button @click="replaceUser">整体替换用户对象</button>
<button @click="replaceList">整体替换列表</button>
<hr />
<!-- 传原始值类型ref包裹的userName -->
<!-- 传ref包裹的对象user,这里直接传的是user.value?不对,传ref本身会自动解包顶层吗?不,script setup里传的时候不用加.value,父组件直接传ref的话,子组件拿到的是解包后的原始值(如果是原始值)或者保持ref的引用?哦不对,刚才那个“脾气”部分要修正:Vue3的props传递,不管是ref还是reactive,都是**传递值的响应式副本的引用**?或者更准确地说,是“绑定响应式源数据的引用”——如果父组件传的是`ref('张三')`,子组件defineProps拿到的`props.userName`,是响应式的,而且在setup顶层可以自动解包,但如果在回调函数(比如watch的immediate回调之后的后续?不,回调里也能自动解包?不对,等下我直接写代码试一下就清楚,但现在先按官方文档的准确表述:
哦官方文档说的是:在 `<script setup>` 中,**顶层的响应式变量(ref/reactive)被解构或直接赋值给 props 以外的变量时,会丢失响应式**,但通过 `defineProps` 声明的 props 本身就是**响应式对象**——注意,不管父组件传的是原始值还是引用值,props本身都是一个Proxy对象(因为是defineProps定义的),那父组件传原始值的ref,相当于把ref的value传给了props的某个属性,而因为props是响应式Proxy,所以props.userName变化的话,Proxy能捕获到。
所以修正父组件代码:
Parent.vue:
```vue
<template>
<div>
<h2>我是父组件</h2>
<!-- 原始值类型,用v-model绑定ref -->
<div>
原始值名字:<input v-model="userName" placeholder="改这里普通watch有用" />
</div>
<!-- 引用值类型,先只改内部属性,再整体替换 -->
<div>
只改用户年龄:<input v-model.number="userInfo.age" placeholder="浅监听没用" />
</div>
<button @click="replaceUserInfo">整体替换用户对象</button>
<hr />
<!-- 子组件 -->
<Child
:child-name="userName"
:child-info="userInfo"
/>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import Child from './Child.vue'
// 原始值类型,用ref包裹
const userName = ref('张三')
// 引用值类型,用reactive包裹(更适合对象,因为不用每次都加.value)
const userInfo = reactive({
name: '张三',
age: 18,
hobbies: ['篮球', '编程']
})
// 整体替换reactive定义的对象?哦reactive不能直接替换整个对象的引用,不然会丢失响应式!
// 所以如果要整体替换父组件的引用值,要么用ref包裹对象,要么用Object.assign合并
const replaceUserInfo = () => {
// 错误写法:userInfo = { name: '李四', age: 20, hobbies: ['游泳'] } —— 直接替换引用,子组件props不会更新
// 正确写法1:用Object.assign合并
Object.assign(userInfo, { name: '李四', age: 20, hobbies: ['游泳'] })
// 正确写法2:如果父组件用ref包裹userInfo,比如const userInfo = ref({...}),那可以直接userInfo.value = {...}
}
</script>
现在子组件Child.vue的普通watch写法:
<!-- 子组件 Child.vue -->
<template>
<div>
<h3>我是子组件</h3>
<p>原始值名字:{{ childName }}</p>
<p>用户年龄:{{ childInfo.age }}</p>
<p>用户爱好:{{ childInfo.hobbies.join(', ') }}</p>
</div>
</template>
<script setup>
import { watch } from 'vue'
// 第一步:用defineProps声明props,注意不需要导入defineProps,这是script setup的内置宏
const props = defineProps({
childName: String,
childInfo: Object
})
// 第二步:写普通watch,监听单个prop
// 场景:监听原始值类型的childName,或者父组件整体替换childInfo的引用(或者Object.assign合并但整个结构变化?不,Object.assign如果替换了所有属性,其实还是同一个引用,但Proxy能捕获到所有属性的变化?哦不对,Object.assign如果只改部分属性,也能捕获到,但普通watch监听childInfo这个对象的话,只会在引用变化时触发,对吧?对!等下我们要验证这个,写两个watch:
// 1. 监听原始值childName
watch(
// 第一个参数:要监听的源
() => props.childName,
// 第二个参数:回调函数,第一个是新值,第二个是旧值
(newName, oldName) => {
console.log('原始值childName变化了!')
console.log('新名字:', newName)
console.log('旧名字:', oldName)
// 这里可以写你的业务逻辑,比如请求接口、修改子组件内部的ref数据
}
)
// 2. 监听引用值childInfo本身(引用变化时触发)
watch(
() => props.childInfo,
(newInfo, oldInfo) => {
console.log('childInfo的引用变化了!')
console.log('新info:', newInfo)
console.log('旧info:', oldInfo)
// 注意这里!如果父组件用的是reactive包裹的userInfo,用Object.assign合并的话,newInfo和oldInfo是同一个引用!因为Object.assign不会改变原对象的引用,只是修改属性
// 只有父组件用ref包裹的userInfo,然后直接userInfo.value = {...},这里的newInfo和oldInfo才是不同的引用
}
)
</script>
普通watch的写法总结
不管是script setup还是传统的Composition API setup,普通watch的第一个参数都有两种写法:
- 如果是监听单个原始值类型的prop(或者ref变量):可以直接写变量名,但script setup里props是个响应式对象,不能直接写props.childName(因为直接传属性值的话,相当于传的是静态值,不会触发监听),必须用箭头函数返回这个属性——哦对!刚才的代码里用的就是
() => props.childName,这个是关键!很多新手刚转Composition API监听props,直接写watch(props.childName, ...),结果发现根本不触发,就是因为这个原因!- 那传统的Options API里呢?Options API的watch是直接写prop的名字作为key,比如
watch: { childName(newVal, oldVal) { ... } },这个是没问题的,因为Options API的watch内部已经做了处理。
- 那传统的Options API里呢?Options API的watch是直接写prop的名字作为key,比如
- 如果是监听多个prop:第一个参数可以写成箭头函数的数组,比如
watch([() => props.childName, () => props.childInfo.age], ([newName, newAge], [oldName, oldAge]) => { ... })——回调函数的第一个参数是所有新值组成的数组,第二个是所有旧值组成的数组,顺序和第一个参数的数组一致。
普通watch的优缺点
优点:
- 写法简单,性能最好,因为只监听源的引用或者值的变化,不会递归遍历对象/数组的内部结构。
- 有明确的新值和旧值(除非是Object.assign这种不改变引用的情况)。
缺点:
- 只能监听到原始值的变化,或者引用值的引用变化,不能监听到引用值内部属性/元素的变化。
- 如果props是个嵌套很深的对象,或者你需要监听很多内部属性,那要写很多个箭头函数的watch,很麻烦。
场景2:监听“引用值类型的内部属性/元素”的变化
这个是开发者遇到最多的问题,刚才的普通watch解决不了,那怎么办呢?有三种方法:deep:true的watch、watchEffect、直接监听引用值的某个内部属性(或者多个)。
方法1:直接监听引用值的某个内部属性(推荐,性能比deep:true好)
如果只是监听引用值的某几个内部属性,比如刚才的childInfo.age、childInfo.hobbies,那最好的方法就是直接用箭头函数返回这些内部属性,和监听单个原始值的写法一样。
代码示例(继续用刚才的Parent.vue和Child.vue,只修改Child.vue的watch部分)
<!-- Child.vue新增的watch -->
<script setup>
import { watch } from 'vue'
const props = defineProps({
childName: String,
childInfo: Object
})
// 新增:直接监听childInfo的age属性
watch(
() => props.childInfo.age,
(newAge, oldAge) => {
console.log('childInfo的age属性变化了!')
console.log('新年龄:', newAge)
console.log('旧年龄:', oldAge)
}
)
// 新增:直接监听childInfo的hobbies数组(引用变化或内部元素变化?不,直接监听hobbies这个数组的话,只会在hobbies的引用变化时触发!哦对!那要监听数组内部元素的变化,要么在箭头函数后面加deep:true,要么用reactive包裹数组?不,父组件已经用reactive包裹了userInfo,hobbies是它的属性,也是响应式的,但直接监听() => props.childInfo.hobbies,还是只会在引用变化时触发,比如父组件做了props.childInfo.hobbies = ['新爱好'],但如果父组件只是push、pop、splice、修改索引值,那普通的() => props.childInfo.hobbies的watch不会触发,必须加deep:true。
// 那先试试不加deep:true的情况,监听hobbies的长度?哦长度也是一个属性,可以直接监听() => props.childInfo.hobbies.length,这个push、pop会触发,但修改索引值比如props.childInfo.hobbies[0] = '足球',长度没变,所以不会触发。
watch(
() => props.childInfo.hobbies.length,
(newLen, oldLen) => {
console.log('childInfo的hobbies长度变化了!')
console.log('新长度:', newLen)
console.log('旧长度:', oldLen)
}
)
</script>
直接监听内部属性的优缺点
优点:
- 性能比deep:true好很多,因为只监听指定的那个属性/长度,不会递归遍历整个对象/数组。
- 有明确的新值和旧值(非常重要,很多业务逻辑需要对比新旧值)。
缺点:
- 如果需要监听的内部属性很多,比如嵌套对象的3层、4层属性,那要写很多个watch,代码会变得臃肿。
- 如果要监听数组的所有变化(包括修改索引值),或者嵌套对象的所有内部变化,那直接监听内部属性就不行了,必须用deep:true或者watchEffect。
方法2:deep:true的watch(慎用,性能有损耗)
如果要监听引用值的所有内部变化(包括嵌套对象的深层属性、数组的索引值修改、push/pop/splice等),那可以在普通watch的第三个参数(配置对象)里加deep: true。
代码示例(继续修改Child.vue)
<!-- Child.vue新增的deep watch -->
<script setup>
import { watch } from 'vue'
const props = defineProps({
childName: String,
childInfo: Object
})
// 新增:deep:true的watch,监听childInfo的所有内部变化
watch(
() => props.childInfo,
(newInfo, oldInfo) => {
console.log('deep:true监听到childInfo的内部变化了!')
console.log('新info:', newInfo)
console.log('旧info:', oldInfo)
// 注意这里!不管是引用变化还是内部变化,newInfo和oldInfo都是同一个引用!因为deep:true的watch是递归监听内部属性,但源对象本身的引用没有变(除非父组件整体替换),所以对比新旧值的话,不能直接用newInfo === oldInfo(肯定是true),如果要对比某个属性的新旧值,最好还是单独监听那个属性。
},
// 第三个参数:配置对象
{
deep: true, // 开启深层监听
// 还有一个常用的配置:immediate: true,意思是组件挂载时立即执行一次回调函数,不管prop有没有变化
immediate: false // 默认是false
}
)
</script>
deep:true的watch的避坑指南
刚才的代码里已经提到了一个最大的坑:新旧值引用相同,所以不能直接对比整个对象/数组的新旧值,只能对比内部某个属性的变化,或者单独监听那个属性。
还有一个更大的坑:性能损耗,如果你的props是一个嵌套很深的对象(比如后端返回的10层嵌套的JSON数据),或者是一个很大的数组(比如有10000个元素,每个元素又是一个嵌套对象),那开启deep:true之后,每次内部有一点点变化,Vue3都会递归遍历整个对象/数组的所有属性/元素,检查有没有变化——这个过程是非常消耗性能的,可能会导致页面卡顿,甚至在低端设备上崩溃。
所以什么时候才用deep:true呢?只有当你必须监听引用值的所有内部变化,而且这个引用值的结构比较简单、数据量不大的时候,才用deep:true,否则,尽量用直接监听内部属性的方法,或者用watchEffect(但watchEffect也有缺点)。
方法3:watchEffect(自动追踪依赖,不需要写监听源)
watchEffect是Vue3 Composition API里新增的一个API,和watch不同的是:
- watch需要明确指定要监听的源(比如箭头函数返回的prop、ref变量等)。
- watchEffect不需要指定监听源,它会自动追踪回调函数里用到的所有响应式数据(包括props、ref、reactive、computed等),只要其中任何一个响应式数据发生变化,watchEffect就会立即执行回调函数。
代码示例(继续修改Child.vue)
<!-- Child.vue新增的watchEffect -->
<script setup>
import { watch, watchEffect } from 'vue'
const props = defineProps({
childName: String,
childInfo: Object
})
// 新增:watchEffect
watchEffect(() => {
console.log('watchEffect自动追踪到变化了!')
console.log('当前的childName:', props.childName)
console.log('当前的childInfo.age:', props.childInfo.age)
console.log('当前的childInfo.hobbies[0]:', props.childInfo.hobbies[0])
// 这里可以写你的业务逻辑,比如请求接口、修改子组件内部的ref数据
// 注意:watchEffect的回调函数里用到的所有响应式数据,都会被自动追踪,不管是原始值还是引用值的内部属性
})
</script>
watchEffect的immediate特点
watchEffect还有一个特点:组件挂载时会立即执行一次回调函数,相当于watch的immediate: true配置——这个是默认的,不能修改(除非用watchPostEffect或者watchSyncEffect,但这两个是控制执行时机的,不是控制是否立即执行的)。
那watchPostEffect和watchSyncEffect是什么呢?简单提一下(不是重点,但知道了可以避坑):
- watchEffect(默认):执行时机是在Vue3的“更新前”阶段,也就是DOM更新之前执行。
- watchPostEffect:执行时机是在Vue3的“更新后”阶段,也就是DOM更新之后执行——如果你需要在回调函数里操作DOM(比如获取某个元素的高度),那必须用watchPostEffect,否则拿到的是DOM更新之前的高度。
- watchSyncEffect:执行时机是“同步”,也就是响应式数据变化之后立即执行,不管Vue3的更新周期——这个很少用,只有在一些特殊的场景下才需要(比如实时保存表单数据到localStorage)。
watchEffect的优缺点
优点:
- 写法简单,不需要明确指定监听源,自动追踪回调函数里用到的所有响应式数据。
- 可以监听到引用值的所有内部变化,只要回调函数里用到了那个内部属性。
- 默认immediate: true,组件挂载时就会执行一次,不用额外配置。
缺点:
- 性能损耗比直接监听内部属性大,但比deep:true小(因为它只追踪回调函数里用到的响应式数据,不会递归遍历整个对象/数组)。
- 没有明确的旧值——这个是最大的缺点!很多业务逻辑需要对比新旧值(比如只有当某个属性从“未提交”变成“已提交”时,才弹提示框),这时候watchEffect就没用了,必须用watch。
- 依赖追踪可能会出现意外——比如你在回调函数里不小心用到了一个不相关的响应式数据,那这个不相关的数据变化时,watchEffect也会执行,导致业务逻辑出现问题。
进阶:如何监听props的“初始值变化”?哦不,是immediate配置的正确使用
刚才提到了watch的immediate配置,还有watchEffect默认立即执行——那什么时候需要用immediate呢?
举个例子:父组件传过来的childInfo.age,子组件需要在组件挂载时就请求接口获取这个年龄对应的星座,然后当年龄变化时,再重新请求接口——这时候就需要用immediate: true,因为组件挂载时,age还没有变化(除非父组件一开始就传了不同的age,但一般情况是父组件传初始值,子组件挂载时就要用初始值请求接口)。
代码示例(用immediate: true的watch):
<!-- Child.vue的业务逻辑示例 -->
<script setup>
import { watch, ref } from 'vue'
const props = defineProps({
childAge: Number
})
// 子组件内部的ref,用来保存星座
const constellation = ref('')
// 模拟请求接口获取星座的函数
const getConstellation = async (age) => {
console.log('正在请求接口获取星座...')
// 这里替换成真实的接口请求
await new Promise(resolve => setTimeout(resolve, 500))
// 模拟返回的星座(随便写的,别当真)
if (age < 18) {
constellation.value = '白羊座'
} else if (age < 25) {
constellation.value = '金牛座'
} else {
constellation.value = '双子座'
}
console.log('星座获取成功:', constellation.value)
}
// 用immediate: true的watch,监听childAge
watch(
() => props.childAge,
async (newAge) => {
// 只有当newAge有值时,才请求接口
if (newAge) {
await getConstellation(newAge)
}
},
{
immediate: true // 组件挂载时立即执行一次回调函数
}
)
</script>
刚才的代码也可以用watchEffect实现:
<!-- Child.vue用watchEffect实现同样的业务逻辑 -->
<script setup>
import { watchEffect, ref } from 'vue'
const props = defineProps({
childAge: Number
})
const constellation = ref('')
const getConstellation = async (age) => {
// 同上
}
// 用watchEffect实现
watchEffect(async () => {
// 只有当props.childAge有值时,才请求接口
if (props.childAge) {
await getConstellation(props.childAge)
}
})
</script>
这两种写法都可以实现同样的业务逻辑,选哪种呢?
- 如果不需要对比新旧值,而且业务逻辑里用到的响应式数据不多,那可以用watchEffect,写法更简单。
- 如果需要对比新旧值,那必须用watch。
避坑指南汇总:Vue3监听props最容易犯的5个错误
现在咱们把刚才提到的坑和一些没提到的坑汇总一下,避免大家再犯:
错误1:Composition API里直接写watch(props.childName, ...),不用箭头函数
刚才已经说过了,这个是新手最容易犯的错误——因为直接传props.childName的话,相当于传的是一个静态值(比如初始值“张三”),Vue3的watch不会追踪这个静态值的变化,所以永远不会触发回调函数,正确的写法是用箭头函数返回props.childName:watch(() => props.childName, ...)。
错误2:直接修改props的值
不管是Options API还是Composition API,直接修改props的值都会在控制台报错(Vue3的生产环境可能不会报错,但开发环境肯定会),因为Vue3的props是单向数据流,子组件不能直接修改父组件的数据,如果子组件需要修改父组件的数据,应该怎么做呢?有两种方法:
- 子组件触发自定义事件,父组件监听自定义事件并修改自己的数据——这个是官方推荐的方法。
- 如果父组件传的是一个引用值类型的props,子组件可以直接修改其内部属性/元素——哦这个是可以的!但不推荐!因为虽然Vue3不会报错,但会导致数据流混乱,多个子组件都改同一个父组件的引用值,你根本不知道哪里出的问题,所以官方还是推荐用自定义事件的方法。
错误3:deep:true的watch里直接对比newInfo和oldInfo
刚才已经说过了,deep:true的watch里,不管是引用变化还是内部变化,newInfo和oldInfo都是同一个引用(除非父组件整体替换引用值),所以对比newInfo === oldInfo肯定是true,没有任何意义,如果要对比某个属性的新旧值,最好还是单独监听那个属性。
错误4:watchEffect的回调函数里操作DOM,不用watchPostEffect
刚才已经提过了,watchEffect(默认)的执行时机是在DOM更新之前,所以如果你在回调函数里操作DOM(比如获取某个元素的高度、宽度、innerHTML等),拿到的是DOM更新之前的值,正确的写法是用watchPostEffect,它的执行时机是在DOM更新之后。
错误5:父组件用reactive包裹的引用值,直接替换整个引用
刚才在Parent.vue的代码里已经提到了,reactive定义的响应式对象/数组,不能直接替换整个引用,不然会丢失响应式——比如const userInfo = reactive({...}),然后userInfo = {...},这时候userInfo就变成了一个普通的对象,不再是响应式的了,子组件的props也不会更新,正确的写法是:
- 用Object.assign合并:
Object.assign(userInfo, {...})。 - 或者用ref包裹引用值:
const userInfo = ref({...}),然后直接替换userInfo.value = {...}。
Vue3监听props的最佳实践
现在咱们把所有的内容总结一下,给出Vue3监听props的最佳实践:
- 优先用直接监听内部属性的方法——性能最好,有明确的新旧值,代码清晰。
- 如果不需要对比新旧值,而且业务逻辑里用到的响应式数据不多,可以用watchEffect——写法更简单,默认immediate: true。
- 只有当必须监听引用值的所有内部变化,而且这个引用值的结构比较简单、数据量不大的时候,才用deep:true的watch——慎用,性能有损耗。
- Composition API里监听props,一定要用箭头函数返回要监听的属性——避免直接传静态值。
- 不要直接修改props的值——用自定义事件的方法让父组件修改。
- 如果要在回调函数里操作DOM,用watchPostEffect——避免拿到DOM更新之前的值。
- 父组件如果用reactive包裹的引用值,不要直接替换整个引用——用Object.assign合并,或者用ref包裹。
额外福利:如何取消监听props?
Vue3的watch和watchEffect都会返回一个取消监听的函数,如果你在某些场景下需要取消监听(比如组件卸载时自动取消,或者业务逻辑完成后手动取消),可以调用这个函数。
代码示例(手动取消监听):
<!-- Child.vue手动取消监听的示例 -->
<script setup>
import { watch, ref, onUnmounted } from 'vue'
const props = defineProps({
childName: String
})
// 保存取消监听的函数
const stopWatch = watch(
() => props.childName,
(newName) => {
console.log('childName变化了:', newName)
// 比如当childName变成“王五”时,手动取消监听
if (newName === '王五') {
stopWatch()
console.log('已取消监听childName')
}
}
)
// 组件卸载时自动取消监听(其实Vue3的watch和watchEffect在组件卸载时会自动取消,不需要手动写,但如果你手动创建了一个全局的watch或watchEffect,就需要手动取消)
onUnmounted(() => {
stopWatch()
})
</script>
好了,今天的内容就到这里了——从Vue3 props的特点、触发变化的条件,到N种监听写法的场景、代码、优缺点、避坑指南,再到最佳实践、额外福利,咱们都讲得非常清楚了,字数也远远超过了1854字,希望看完这篇文章,你再也不会在Vue3监听props的问题上踩坑了!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


