vue3组合式api的函数系列一

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 函数的返回值。

    • 也可以接受一个带有 getset 函数的对象来创建一个可写的 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没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac如何卸载应用并删除文件,2023年最新妙招大公开!

大家好&#xff0c;今天小编要为大家分享一些关于mac电脑的小技巧&#xff0c;特别是关于如何正确卸载应用程序以及清理卸载后的残留文件。你知道吗&#xff1f;很多人都不知道&#xff0c;mac系统默认的卸载方式可能会导致一些残留文件滞留在你的电脑上&#xff0c;慢慢地占用…

解决 ARouter 无法生成路由表,Toast提示 找不到目标路由

Android Studio 版本&#xff1a;2022.3.1 ARouter 版本&#xff1a;1.5.2 1、先检查 项目路径&#xff0c;是否有中文&#xff0c;不要有中文&#xff1b; 2、加载注解库&#xff0c;使用 kapt&#xff0c;不要用 annotationProcessor。 3、分模块开发&#xff0c;每个需要…

openGauss学习笔记-86 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT部署配置

文章目录 openGauss学习笔记-86 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT部署配置86.1 总体原则86.2 重做日志&#xff08;MOT&#xff09;86.3 检查点&#xff08;MOT&#xff09;86.4 恢复&#xff08;MOT&#xff09;86.5 统计&#xff08;MOT&#xff09;86…

进入IT行业:选择前端开发还是后端开发?

一、前言 开发做前端好还是后端好&#xff1f;这是一个常见的问题&#xff0c;特别是对于初学者来说。在编程世界中&#xff0c;前端开发和后端开发分别代表着用户界面和数据逻辑&#xff0c;就像城市的两个不同街区一样。但是&#xff0c;究竟哪个街区更适合我们作为开发者呢…

第一百六十回 SliverPadding组件

文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了SliverAppBar组件相关的内容&#xff0c;本章回中将介绍 SliverPadding组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的SliverPadding组件类似Pading组件&#xff0c;它主要用…

Mapfree智驾方案,怎样实现成本可控?

整理|睿思 编辑|祥威 编者注&#xff1a;本文是HiEV出品的系列直播「智驾地图之变」第二期问答环节内容整理。 元戎启行副总裁刘轩与连线嘉宾奥维咨询董事合伙人张君毅、北汽研究总院智能网联中心专业总师林大洋、主持嘉宾周琳展开深度交流&#xff0c;并进行了答疑。 本期元…

【算法|贪心算法系列No.3】leetcode334. 递增的三元子序列

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【Leetcode】166.分数到小数

一、题目 1、题目描述 给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。 如果小数部分为循环小数,则将循环的部分括在括号内。 如果存在多个答案,只需返回 任意一个 。 对于所有给定的输入,保证 答案字符串的长度小于 104 。…

oracle分组合并数值带顺序

比如&#xff1a;有如下一张设备电子围栏位置坐标的表&#xff08;tb_equ_point&#xff09;。 equ_name:设备电子围栏名称 point_id:点位坐标id point_x:点位x坐标 point_y:点位y坐标。 附数据&#xff1a; INSERT INTO "tb_equ_point" ("EQU_NAME",…

番外3:下载+安装VMware(前期准备)

step1: 查看自己笔记本电脑配置&#xff1b; step2: 下载并安装VMware&#xff08;下载地址www..kkx.net/soft/16841.html&#xff09;这里选择本地普通下载&#xff1b; step3: 安装VMware过程中需要填写密钥&#xff08;本人用的最后一个&#xff09;; #UU54R-FVD91-488PP-7N…

QUIC不是TCP的替代品

QUIC取代了TCP成为HTTP3的基础传输协议&#xff0c;不是因为QUIC能够取代TCP的所有应用场景&#xff0c;而是因为QUIC更适合HTTP的请求/响应业务模型。原文: QUIC Is Not a TCP Replacement TCP新规范(RFC 9293)的发布是网络界的一件大事&#xff0c;值得围绕这一主题发表第二篇…

Android开源库

见&#xff1a;GitHub - eHackyd/Android_OpenSourceLibrary: Android开源库的学习笔记

友思特案例|友思特 Ensenso 3D相机:汽车工业自动化的革命性力量

01 内容摘要 在竞争激烈的汽车行业&#xff0c;自动化生产至关重要。友思特 Ensenso 3D相机为汽车制造商提供了可靠的工具和技术支持&#xff0c;助力多个关键环节。它在汽车座位泡棉切割中提高精确度&#xff0c;降低浪费&#xff0c;提高生产效率&#xff1b;在汽车压铸零部…

kill -9 的用法

kill -9 是一个 Linux 命令&#xff0c;用于强制终止一个进程。当你在终端中输入 kill -9 <PID> 并按下回车后&#xff0c;系统会发送一个强制终止信号给指定进程的进程 ID&#xff08;PID&#xff09;&#xff0c;以立即终止该进程。 需要注意的是&#xff0c;使用 kil…

<泛型>带你更详细的认识泛型

了解泛型 现在有一个需求&#xff1a;写一个打印类&#xff0c;用来打印不同类型的数据 //类1 &#xff1a;打印Integer类型的数据 public class IntegerPrint {Integer content;public void Integer(Integer content) {this.content content;}public void print(){System.o…

Python3数据科学包系列(二):数据分析实战

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 一&#xff1a;通过read_table函数读取数据创建(DataFrame)数据框 #…

在服务器上使用nginx改变前端项目请求的url

location /app-dev {rewrite ^/app-dev/(.*) /$1 break;proxy_pass http://152.136.36.251:9999;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr; } location /请求后缀 { rewrite ^/app-dev/(.*) /$1 break; proxy_pass 想要的请求后端的url; …

九、GC收集日志

JVM由浅入深系列一、关于Java性能的误解二、Java性能概述三、了解JVM概述四、探索JVM架构五、垃圾收集基础六、HotSpot中的垃圾收集七、垃圾收集中级八、垃圾收集高级👋GC收集日志 ⚽️1. 认识GC收集日志 垃圾收集日志是一个重要的信息来源,对于与性能相关的一些悬而未决的…

Ubuntu22无法自动进入lightdm图像界面

问题&#xff1a;Ubuntu22无法自动进入lightdm图像界面&#xff0c;必须手动运行 lightdm start解决方案&#xff1a; 方案一&#xff1a; 运行一个终端输入 cat /etc/X11/default-display-manager /etc/init/lightdm.conf不接受lightdm作为设置&#xff0c;但是&#xff0c;/…

1.5.C++项目:仿muduo库实现并发服务器之socket模块的设计

项目完整版在&#xff1a; 一、socket模块&#xff1a;套接字模块 二、提供的功能 Socket模块是对套接字操作封装的一个模块&#xff0c;主要实现的socket的各项操作。 socket 模块&#xff1a;套接字的功能 创建套接字 绑定地址信息 开始监听 向服务器发起连接 获取新连接 …