第十一节: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>
View Code

 

三. 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>
View Code

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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2021-10-05 19:49  Yaopengfei  阅读(497)  评论(1编辑  收藏  举报