1、响应式核心
1)、 ref(值)
1)、功能:接受值,返回一个响应式的、可更改的 ref 对象,ref对象只有一个属性:value
。value属性保存着接受的值。
2)、使用ref对象:模板上不需要写 .value 属性(会自动解构),在js中,使用 .value 来完成数据的读写。
3)、ref可以接收基本类型和引用类型
-
ref可以接收基本类型。
-
ref也可以接收引用类型:如果将一个对象传给 ref函数,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。
const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1
示例
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"><!-- ref响应对象,在模板上使用时,不需要.value,因为,在模板上vue会自动处理 --><p>msg:{{msg}}</p><input type="button" value="修改msg" @click="changeMsg"></div> </body> </html> <script src="./js/vue.global.js"></script> <script> // ref:对应着选项式api中data(data中的数据都是响应式的)const {createApp,ref} = Vue; let app = createApp({setup(){// ref()函数会返回一个响应式对象,该响应式对象的value属性,保存着数据的值// 1、在模板使用时,不需要 .value// 2、在js中使用时,需要 .valuelet msg=ref("hello");//ref函数返回一个响应式的对象,该对象的value属性的值是"hello"console.log("msg",msg);console.log("msg.value",msg.value); function changeMsg(){msg.value+="1";} return {msg,changeMsg}}}) app.mount("#app"); </script>
以后创建 非 对象类型的数据 使用 ref, 创建对象类型的数据建议使用 reactive
2)、 reactive(对象)
1)、功能: 接受一个对象,返回一个对象的响应式代理(proxy)。返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。【解释一下”解包“,也就是说不用 .value。或者说读取ref属性时,自动会把.value属性的值拿到。】
2)、注意点:当访问到某个响应式数组或 Map
这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包【还得使用.value】。
创建一个响应式对象:
const obj = reactive({ count: 0 }) obj.count++
2.1)、reactive的基本使用,对象使用reactive
reactive示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"><!-- // 1、引用类型的数据使用reactive --><p>person.name:{{person.name}}</p><p>person.sex:{{person.sex}}</p><p>person.age:{{person.age}}</p><input type="button" value="修改age" @click="changeAge"><p>person.wife.age:{{person.wife.age}}</p><input type="button" value="修改wife.age" @click="changeWifeAge"><hr/><!-- // 2、引用类型的数据使用ref --><p>book.name:{{book.name}}</p><p>book.price:{{book.price}}</p><input type="button" value="修改价格" @click="changePrice"><p>book.author.age:{{book.author.age}}</p><input type="button" value="修改作者的age" @click="changeAuthorAge"><hr/><!-- // 3、基本类型使用reactive:不会成为响应式 --><p>msg:{{msg}}</p><input type="button" value="修改Msg" @click="changeMsg"></div> </body> </html> <script src="./js/vue.global.js"></script> <script> // reactive:对应着选项式api中data,一般使用在对象上// ref也可以让对象成为响应式的,只不过需要多些一个value。 const {createApp,ref,reactive} = Vue; let app = createApp({setup(){ // reactive()函数会返回代理对象Proxy。// 1、引用类型的数据使用reactivelet person = reactive({name:"王义鑫",sex:"男",age:18,wife:{name:"范冰冰",age:16}}); console.log("person",person); //Proxy对象 function changeAge(){person.age++;}function changeWifeAge(){person.wife.age++;}// 2、引用类型的数据使用reflet book = ref({name:"三国演义",price:51.2,author:{name:"罗贯中",age:108,address:"南窑头国际"}}); function changePrice(){book.value.price ++;}function changeAuthorAge(){book.value.author.age++;} // 3、基本类型使用reactive:不会成为响应式let msg = reactive("hello");//如果给reactive传入基本类型的数据,那么,返回值就是基本类型console.log("msg",typeof msg);//string。肯不是响应式的了 function changeMsg(){msg +="1";console.log("changeMsg函数",msg);} return {person,changeAge,changeWifeAge,book,changePrice,changeAuthorAge,msg,changeMsg}}}) app.mount("#app"); </script>
2.2)把ref对象赋给reactive,会自动解包
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <p>countRef:{{countRef}}</p><p>personReactive.count:{{personReactive.count}}</p><input type="button" value="修改count" @click="changeCount"></div> </body> </html> <script src="./js/vue.global.js"></script> <script> // 把ref对象赋给reactive,会自动解包。// 进一步解释:把ref对象作为某个对象的属性值,并把该对象传给reactive,那么ref会自动解包 const {createApp,ref,reactive} = Vue; let app = createApp({setup(){ // 1、定义一个ref对象let countRef = ref(12);// 2、把ref对象作为某个对象的属性值,并把该对象传给reactive,// 1)、那么ref会自动解包(把value属性取出来)// 2)、ref和对象的属性形成了关联关系。let personReactive = reactive({count:countRef //会解包,并且还会把count属性和countRef对象关联起来。}); console.log("personReactive.count",personReactive.count);//12;其实是代理对象读取count属性时,调用了get,get函数里返回的是ref对象的value属性console.log(personReactive.count===countRef);//falseconsole.log(personReactive.count===countRef.value);//true function changeCount(){personReactive.count++;//修改reactive对象的属性时,ref的值也会变化。console.log("countRef.value",countRef.value);} // 3、如果没有reactive,肯定不会解包let obj = {count:countRef} console.log("obj.count",obj.count);//ref对象 return {countRef,personReactive,changeCount}}}) app.mount("#app"); </script>
2.3)、注意:ref类型的数组元素不会解构。
当访问到某个响应式数组或 Map
这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包(模板上也不会解包):
const books = reactive([ref('Vue 3 Guide'),ref(5)])//给reactive传入的是数组。不会解包 // 这里需要 .value console.log(books[0].value)//'Vue 3 Guide' console.log(books[1].value)//5 const map = reactive(new Map([['count', ref(0)]]))//给reactive传入的是Map。不会解包 // 这里需要 .value console.log(map.get('count').value)
示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <ul><li v-for="item in booksReactive"><!-- 如果reactive对象里写的是ref数组时。显示ref对象的值时,需要使用 .value--><p>{{item.value}}</p></li></ul><input type="button" value="添加一本书" @click="addBook"></div> </body> </html> <script src="./js/vue.global.js"></script> <script> // reactive接收ref的数组时,不会解包 const {createApp,ref,reactive} = Vue; let app = createApp({setup(){ let bookRef1 = ref("三国演义");let bookRef2 = ref("红楼梦"); let booksReactive = reactive([bookRef1,bookRef2]);console.log("booksReactive",booksReactive); console.log("booksReactive[0]",booksReactive[0]);//ref对象console.log("booksReactive[1]",booksReactive[1]);//ref对象 console.log("booksReactive[0].value",booksReactive[0].value); function addBook(){booksReactive.push(ref("西游记"));} return {booksReactive,addBook}}}) app.mount("#app"); </script>
reactive 和 ref的选用:
对象用reactive,其它用ref
3)、 readonly()
1)、功能:接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
2)、只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive()
相同,但解包得到的值是只读的。
const original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => {// 用来做响应性追踪console.log(copy.count) }) // 更改源属性会触发其依赖的侦听器 original.count++ // 更改该只读副本将会失败,并会得到一个警告 copy.count++ // warning
示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <p>bookReadonly.name:{{bookReadonly.name}}</p><p>bookReadonly.author.name:{{bookReadonly.author.name}}</p><input type="button" value="修改" @click="changeName" ></div> </body> </html> <script src="./js/vue.global.js"></script> <script> // readonly 接收一个对象(响应式对象,普通对象)和ref。让其成为只读的,而且,只读是深层次的。 // 原生中学习的const的只读,只限定变量对应的内存区域是只读的。const p = {name:"王义鑫"} p = {name:"罗怡欣"};//不能改的console.log("p.name",p.name);//王义鑫 p.name = "张伟业";//可以console.log("p.name",p.name);//张伟业 const {createApp,ref,reactive,readonly} = Vue; let app = createApp({setup(){ let book = {name:"三国演义",author:{name:"老罗",address:"南窑头国际"}} let bookReactive = reactive(book); let bookRef = ref(book); // let bookReadonly = readonly(book);// let bookReadonly = readonly(bookReactive);let bookReadonly = readonly(bookRef); console.log("bookReadonly",bookReadonly); function changeName(){// bookReadonly.name +=1;// console.log("bookReadonly.name",bookReadonly.name);// bookReadonly.author.name += 1;// console.log("bookReadonly.author.name",bookReadonly.author.name); bookReadonly.value.name +=1;console.log("bookReadonly.value.name",bookReadonly.value.name);}return {bookReadonly,changeName}}}) app.mount("#app"); </script>
原生中 const 只能让对象本身只读,不能让对象的属性(包括嵌套属性)只读。
但是,readonly可以让对象的属性(包括嵌套属性)只读
既就是:
const:限制的是:地址(变量对应内存的内容)是只读的。
readonly:限制的是:值(变量引用的内存区域)是只读的
4)、 computed ()
-
功能:computed是计算属性。和选项式api中的计算属性实现的功能一样。
-
参数:
-
可以接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过
.value
暴露 getter 函数的返回值。 -
也可以接受一个带有
get
和set
函数的对象来创建一个可写的 ref 对象。
-
4.1)、创建一个只读的计算属性 ref:
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误
4.2)、创建一个可写的计算属性 ref:
const count = ref(1) const plusOne = computed({get: () => count.value + 1,set: (val) => {count.value = val - 1} })plusOne.value = 1 console.log(count.value) // 0
示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <!-- 1、定义一个只读的计算属性 --><p>count:{{count}}</p><p>doubleCount:{{doubleCount}}</p><input type="button" value="修改count" @click="changeCount" ><hr/><!-- 2、定义一个可读写的计算属性 --> <p>age:{{age}}</p><p>wifeAge:{{wifeAge}}</p><input type="button" value="修改age" @click="changeAge" ><input type="button" value="修改wifeAge" @click="changeWifeAge" > </div> </body> </html> <script src="./js/vue.global.js"></script> <script> const {createApp,ref,reactive,computed} = Vue; let app = createApp({setup(){ // 1、定义一个只读的计算属性let count = ref(5); // 定义一个计算属性:let doubleCount = computed(()=>count.value*count.value); function changeCount(){count.value ++;} // 2、定义一个可读写的计算属性 let age = ref(20); let wifeAge = computed({set:function(newVal){console.log("newVal",newVal);age.value = newVal-3;},get:function(){return age.value+3 }}) function changeAge(){age.value--;}function changeWifeAge(){wifeAge.value--;} return {count,doubleCount,changeCount,age,wifeAge,changeAge,changeWifeAge}}}) app.mount("#app"); </script>
5)、 watch()
watch(第一个参数,第二个参数,第三个参数)
功能:侦听数据的变化,和选项式api中的watch实现的功能一样,组合式api中watch功能更加强大,灵活。默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
参数:
-
第一个参数:侦听器的源,可以是以下几种:
-
一个函数(返回一个值的函数)
-
一个 ref
-
一个reactive
-
...或是由以上类型的值组成的数组
-
-
第二个参数:在(第一个参数的值)发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理(函数)的回调函数。该回调函数(副作用清理的函数)会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如:等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
-
第三个参数:可选的, 是一个对象,支持以下这些选项:
-
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。 -
deep
:如果源是对象,侦听的源(是ref),强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。
-
返回值: 是个函数,该函数可以停止侦听。
与 watchEffect() 相比,watch()
使我们可以:
-
懒执行副作用;
-
更加明确是应该由哪个状态触发侦听器重新执行;
-
可以访问所侦听状态的前一个值和当前值。
-
示例
侦听一个 ref(侦听ref 不用写value):
const count = ref(0) watch(count, (count, prevCount) => {/* ... */ })
侦听一个 getter 函数:
const state = reactive({ count: 0 }) watch(() => state.count,(count, prevCount) => {/* ... */})
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {/* ... */})
当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true }
强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
const state = reactive({ count: 0 })watch(() => state,(newValue, oldValue) => {// newValue === oldValue},{ deep: true })
当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
const state = reactive({ count: 0 })watch(state, () => {/* 深层级变更状态所触发的回调 */})
示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <!-- 1、监听一个ref。 --><p>age:{{age}}</p><p>wifeAge:{{wifeAge}}</p><input type="button" value="修改age" @click="changeAge" ><hr/> <!-- 2、监听一个响应式对象 --> <p>person.name:{{person.name}}</p><p>person.wife.name:{{person.wife.name}}</p><input type="button" value="修改wife的name" @click="changeWifeName" ><hr/> <!-- 3、监听一个回调函数 --> <p>count:{{count}}</p> <input type="button" value="修改count" @click="changeCount" /> <p>inc:{{inc}}</p> <input type="button" value="修改inc" @click="changeInc" /><hr/> <!-- 4、体现deep (当返回值是一个对象时,需要使用deep:true --><p>a:{{objReactive.a}}</p><p>b.b1:{{objReactive.b.b1}}</p><input type="button" value="修改deep" @click="changeDeep"><!-- 5、清除上次的副作用代码--><hr/><input type="text" v-model="value" ><!-- 6、停止侦听--> <hr/><input type="button" value="取消侦听" @click="clearWatch"></div> </body> </html> <script src="./js/vue.global.js"></script> <script> const {createApp,ref,reactive,watch} = Vue; let app = createApp({setup(){ // 1、监听一个ref。 let age = ref(18); let wifeAge = ref(0); watch(age,function(newVal,oldVal){console.log("watch",newVal,oldVal);wifeAge.value = newVal + 3;},{immediate:true}) function changeAge(){age.value--;} // 2、监听一个响应式对象 let person = reactive({name:"王义鑫",age:12,wife:{name:"范冰冰" }})watch(person,function(){console.log("watch person");}) function changeWifeName(){ console.log("changeWifeName");// person = {};//这样修改,不会监听到,因为,你已经让person变成了普通对象,而不是proxy对象 // person.wife = {};//可以// person.wife.name="李冰冰";//可以} // 3、监听一个回调函数let count = ref(12);let inc = ref(2); // 因为回调函数里使用了count和inc,所以,相当于监听了 count和inc的变化watch(()=>count.value+inc.value,function(newVal){// watch([count,inc],function(newVal){console.log("watch监听回调函数newVal",newVal);},{immediate:true});function changeCount(){count.value ++;} function changeInc(){inc.value ++;} // 4、体现deep let objReactive = reactive({a:1,b:{b1:2,b2:3}}) // 当监听的源是回调函数时,只有在 回调函数的返回值发生变化时,才触发watch。// 那么,如果:返回值是引用类型,那么地址不变,返回值就不会变。所以说,改变对象的属性时,并不会引起watch。// 所以说: 当回调函数(侦听源)的返回值是(响应式)对象时。需要使用deep。watch(()=>objReactive,function(){console.log("watch,deep");},{deep:true}) function changeDeep(){console.log("changeDeep");// objReactive.a ++;//并没有改变 objReactive对象,只是改变了对象的属性。objReactive.b.b1 ++;}// 5、清除上次的副作用// 清除上次启动的定时器。const value = ref(""); const stopHandle = watch(value,(newVal,oldVal,onCleanup)=>{ // 发送请求let myTimer = setTimeout(()=>{console.log("响应回来了");},2000) onCleanup(()=>{// 这个函数:实在watch下次触发时,优先调用函数。window.clearTimeout(myTimer);}) }) //6、清除侦听器function clearWatch(){stopHandle();//清除侦听。} return {age,wifeAge,changeAge,person,changeWifeName,count,inc,changeCount,changeInc,objReactive,changeDeep,value,clearWatch}}}) app.mount("#app"); </script>
6)、 watchEffect()
watchEffect
function watchEffect(effect: (onCleanup: OnCleanup) => void,options?: WatchEffectOptions ): StopHandle
-
功能: watchEffect也是监听数据,但是它会立即运行一个函数,而不是懒侦听。watchEffect侦听(依赖)的数据:watchEffect里使用了哪个数据,哪个数据就是watchEffect的依赖。watchEffect是深度侦听的。
-
参数:
-
第一个参数:要运行的副作用函数。这个副作用函数的参数也是一个函数,注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如:等待中的异步请求。(和watch的第二个参数中回调函数的第三参数一样)。
-
-
第二个参数:可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。因为,侦听默认是在vue组件更新前调用,如果你希望组件更新后调用,可以把第二个参数传入:
{ flush: 'post' }
-
返回值:用来停止该副作用的函数。
const count = ref(0) watchEffect(() => console.log(count.value)) // -> 输出 0 count.value++ // -> 输出 1
6.1)、基本使用(副作用)
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <p>age:{{age}}</p><p>isAdult:{{isAdult}}</p><input type="button" value="修改Age" @click="changeAge" ><p>count:{{count}}</p><p>isLimit:{{isLimit}}</p><p>limitChina:{{limitChina}}</p><input type="button" value="修改Count" @click="changeCount" ></div> </body> </html> <script src="./js/vue.global.js"></script> <script> const {createApp,ref,reactive,watch,watchEffect,computed} = Vue; let app = createApp({setup(){ let age = ref(16);let isAdult = ref(false); let count = ref(5);let maxCount = 10;let isLimit = ref(false);//是否达到上限let limitChina = computed(()=>isLimit.value?"不能再买了":"继续买"); // 1、watchEffect回调函数里,使用哪个响应式数据,那么就会监听哪个数据。 watchEffect(()=>{ console.log("watchEffect:age.value",age.value);// 此处写的是 age发生变化时的副作用(即:当age发生变化,还应该做什么事情)isAdult.value = age.value>=18?true:false; // 此处写的是:count发生变化时的副作用。isLimit.value = count.value>=maxCount?true:false; }); // watch(age,function(){// console.log("watch:age.value",age.value);// }) function changeAge(){age.value++;} function changeCount(){if(isLimit.value){return;} count.value++;}return {age,changeAge,isAdult,count,changeCount,isLimit,limitChina}}}) app.mount("#app"); </script>
6.2)、副作用清除:
下面的示例,有点像防抖。
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body> <div id="app"> <p>count:{{count}}</p><p>isLimit:{{isLimit}}</p><input type="button" value="修改" @click="changeCount" ></div> </body> </html> <script src="./js/vue.global.js"></script> <script> const {createApp,ref,reactive,watchEffect} = Vue; let app = createApp({setup(){ let count = ref(1);let isLimit = ref(false);watchEffect((cb)=>{console.log("watchEffect:count.value",count.value); let myTimer = setTimeout(function(){ console.log("修改isLimit的值");isLimit.value = count.value>=10?true:false },2000); // cb里的回调函数是在下次触发副作用时,首先会执行的代码。cb(()=>{console.log("清除定时器");clearTimeout(myTimer);}) }); function changeCount(){count.value++;} return {count,changeCount,isLimit}}}) app.mount("#app"); </script>
6.3)、停止侦听器:
const stop = watchEffect(() => {console.log(count.value)})// 当不再需要此侦听器时:const stopWatch = () => {stop()}
示例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>watchEffect</title> </head> <body><div id="app"><button @click="increment">点击了{{ count }}次</button><p id="p01">p01的内容:{{msg}}</p><button @click="addMsg">点击了addMsg</button><button @click="stopWatch">停止监听</button> <ul><li @click="id=1">请求第一条数据</li><li @click="id=2">请求第二条数据</li><li @click="id=3">请求第三条数据</li></ul></div> </body> <script src="./js/vue.global.js"></script> <script>const { createApp, ref, watchEffect } = Vue const app = createApp({ setup () {const count = ref(0) const increment = () => {count.value += 1} const msg = ref("a"); const addMsg = ()=>{msg.value += 1;} // 返回值可以用来停止侦听const stop = watchEffect(() => {console.log(document.getElementById("p01").innerHTML);//此处打印更新前还是更新后的值,由第二个参数决定console.log(`监听到count的数据为${count.value}`)console.log(`监听到msg的数据为${msg.value}`)},{flush:"post"}) const stopWatch = () => {stop()} const id = ref(1) watchEffect((onCleanup) => {console.log("id.value",id.value) // 关键 --- 引起 当前 watchEffect 二次执行const timer = setTimeout(() => {console.log(`请求第${id.value}条数据`)}, 3000)console.log("timer",timer); // onCleanup的回调函数 会在id更改优先调用。如:下次id更改时,可以对上次的未完成的事情(如:请求)做清除……onCleanup(() => {console.log('onCleanup的回调函数被调用了','清除')clearTimeout(timer);//在id的值更新比较频繁时,只让最后一次请求起作用 }) }) // onInvalidatereturn {count,increment,msg,addMsg,stopWatch,id}}}) app.mount('#app') </script> </html>
watchEffect没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行