zoukankan      html  css  js  c++  java
  • 第十一节: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 来获取。 

    核心代码: 

        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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    n9多媒体不显示图片处理方法
    STM32全球唯一ID读取方法
    VS2008+QT+CYAPI开发USB程序问题
    QT对话框中show和exec的区别
    华硕T20信号差的解决办法
    使用JTAG方式配置EPCS芯片时显示容量不够的解决方法
    QT中使用中文
    MODBUS CRC16
    递归的四条基本法则
    Java代码混淆和加密Jocky
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15369373.html
Copyright © 2011-2022 走看看