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中使用时,需要 .value
let 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、引用类型的数据使用reactive
let 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、引用类型的数据使用ref
let 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);//false
console.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 对象。
- 可以接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 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()
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的值更新比较频繁时,只让最后一次请求起作用
})
})
// onInvalidate
return {
count,
increment,
msg,
addMsg,
stopWatch,
id
}
}
})
app.mount('#app')
</script>
</html>
watchEffect没有具体监听哪一个值的变化,只要内部有某一个状态发生改变就会执行