第十一节:Vue3的Composition Api(toRefs/toRef、customerRef、computed、watchEffect、watch)
一. toRefs/toRef
1. 背景
我们都知道reactive处理的对象是响应式的,但是使用ES6的解构语法对reactive对象进行处理,那么之后无论是修改解构后的变量name、age,还是修改reactive返回的原始对象info1,数据都不再是响应式的。
2. 使用
(1). toRefs
toRefs的函数,可以将reactive返回的对象中的属性都转成ref;如下代码,再次进行结构出来的 name 和 age 本身都是 ref的;
(2). toRef
只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法。
代码分享:
<template> <div> <h3>{{info1.name}}--{{info1.age}}</h3> <h3>{{name}}--{{age}}</h3> <h3>{{name2}}--{{age2}}</h3> <h3>{{age3}}</h3> <h3><button @click="EditAge">修改age</button></h3> </div> </template> <script> import { reactive, toRefs, toRef } from 'vue'; export default { setup(props, context) { // 1. reactive响应式对象 const info1 = reactive({ name: 'ypf', age: 18 }); const info2 = reactive({ name2: 'ypf', age2: 18 }); const info3 = reactive({ name3: 'ypf', age3: 18 }); // 2. 解构后则不再支持响应式 let { name, age } = info1; //3. 借助toRefs和toRef建立响应关系 (ref相关对象要通过.value拿值) let { name2, age2 } = toRefs(info2); let age3 = toRef(info3, "age3"); var EditAge = () => { info1.age++; age++; age2.value++; age3.value++; } return { info1, name, age, name2, age2, age3, EditAge } } } </script>
二. customerRef
1. 说明
创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制:
(1). 它需要一个工厂函数,该函数接受 track 和 trigger 函数作为参数;
(2). 并且应该返回一个带有 get 和 set 的对象;
2. 案例
实现:双向绑定的属性进行debounce(节流)的操作。
useDebounceRef.js
import { customRef } from 'vue'; // 自定义ref export default function(value, delay = 300) { let timer = null; return customRef((track, trigger) => { return { get() { track(); return value; }, set(newValue) { clearTimeout(timer); timer = setTimeout(() => { value = newValue; trigger(); }, delay); } } }) }
调用
<template> <div> <input v-model="msg" /> <h2>{{msg}}</h2> </div> </template> <script> import debounceRef from './hooks/useDebounceRef.js'; export default { setup() { const msg = debounceRef('hello msg'); return { msg } } } </script>
三. computed
1. 说明
计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理。
(1). 在前面的Options API中,我们是使用computed选项来完成的;
(2). 在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
2. 用法
方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;(不能对其修改)
方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象; (可以对其修改)
注:ref对象是响应式对象,如果要获取值,需要 ref.value 来获取,同时也说明computed处理后的数据是支持响应式的。
核心代码:
setup(props, context) { var firstName = ref("Y"); var lastName = ref("PF"); var firstName2 = ref("T"); var lastName2 = ref("MR"); // 方式1: 传入一个getter函数 (写法简介,推荐!) // computed的返回值是一个ref对象,不能对其修改 var fullName1 = computed(() => { return firstName.value + " " + lastName.value; }); // 方式2: 传入一个对象, 对象包含getter/setter,可以对其修改 var fullName2 = computed({ get: () => firstName2.value + " " + lastName2.value, set(newValue) { const names = newValue.split(' '); firstName2.value = names[0]; lastName2.value = names[1]; } }); // 默认的ref支持响应式 var Edit1 = () => { fullName1.value = 'lmr 1'; //修改无效 fullName2.value = 'lmr 2'; //修改有效 } return { fullName1, fullName2, Edit1, } }
剖析:修改fullname1,报警告
四. watchEffect
1. 基本用法
watchEffect用于自动收集响应式数据的依赖
首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
代码分享
<template> <div> <h3>{{name}}</h3> <h3>{{age}}</h3> <h3><button @click="EditName">修改Name</button></h3> <h3><button @click="EditAge">修改Age</button></h3> </div> </template> <script> import { ref, watchEffect } from 'vue'; export default { setup(props, context) { var name = ref('ypf'); var age = ref(10); var EditName = () => name.value = 'ypf2'; var EditAge = () => age.value++; // 开启监听 (默认会执行一次,然后监听后续的每次变化) watchEffect(() => { console.log('开启监听:',name.value,age.value); }); return { name, age, EditName, EditAge } } } </script>
2. setup中使用ref
在setup中如何使用ref或者元素或者组件?
只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;
<template> <div> <h3 ref="myTitleRef">哈哈哈,我是title</h3> </div> </template> <script> import { ref, watchEffect } from 'vue'; export default { setup(props, context) { var myTitleRef = ref(null); return { myTitleRef } } } </script>
3. watchEffect的执行时机
默认值是pre,它会在元素 挂载 或者 更新 之前执行 ,设置成post就执行一次。
<template> <div> <h3 ref="myTitleRef">哈哈哈,我是title</h3> </div> </template> <script> import { ref, watchEffect } from 'vue'; export default { setup(props, context) { var myTitleRef = ref(null); // 默认执行两次,第一次为null,第二次才打印出标签内容 // watchEffect(() => { // console.log(myTitleRef.value); // }); // 执行一次,且直接打印出来内容 watchEffect(() => { console.log(myTitleRef.value); }, { flush: 'post' }); return { myTitleRef } } } </script>
4. 停止监听(了解)
5. 清除副作用(了解)
五. watch
1. 说明
(1). watch的API完全等同于OptionApi watch选项的Property:
A. watch需要侦听特定的数据源,并在回调函数中执行副作用;
B. 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;
(2). 与watchEffect的比较,watch允许我们:
A. 懒执行副作用(第一次不会直接执行);
B. 更具体的说明当哪些状态发生变化时,触发侦听器的执行;
C. 访问侦听状态变化前后的值;
2. 使用
(1). 监听单个数据源
// 1. 监听单个对象 // 1.1 reactive对象-监听其中某个属性,返回的直接就是该属性值 var info1 = reactive({ age: 18, name: 'ypf' }); watch(() => info1.age, (newValue, oldValue) => { console.log(`newValue为:${newValue},oldValue为:${oldValue}`); }); const EditAge = () => { info1.age++; } return { info1, EditAge } //1.2 ref对象,返回的直接就value值 var age = ref(20); watch(age, (newValue, oldValue) => { console.log(`newValue为:${newValue},oldValue为:${oldValue}`); }); const EditAge = () => { age.value++; } return { age, EditAge }
(2). 监听响应式对象
// 2. 监听响应式对象 //2.1 直接监听reactive对象 // 返回值newVale和oldValue还是一个reactive对象 var info1 = reactive({ age: 18, name: 'ypf' }); watch(info1, (newValue, oldValue) => { console.log(`newValue为:${newValue},oldValue为:${oldValue}`); console.log(typeof newValue, typeof oldValue); }); const EditAge = () => { info1.age++; } return { info1, EditAge } // 2.2 如果希望newValue和oldValue是一个普通的对象,可以使用一个getter函数,并且对可响应对象进行解构 var info1 = reactive({ age: 18, name: 'ypf' }); watch(() => { return { ...info1 } }, (newValue, oldValue) => { console.log(`newValue为:${newValue},oldValue为:${oldValue}`); console.log(typeof newValue, typeof oldValue); }); const EditAge = () => { info1.age++; } return { info1, EditAge }
(3). 监听多个数据源
var info1 = reactive({ age: 18, name: 'ypf' }); var age1 = ref(20); watch([() => ({ ...info1 }), age1], ([newInfo, newAge], [oldInfo, oldAge]) => { console.log(newInfo, newAge,oldInfo, oldAge); }); const EditAge = () => { info1.age++; } return { info1, EditAge }
3. 其它参数
深层监听 和 立即执行
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。