Vue3的Composition API

Reactive判断的API

 

isProxy

        检查对象是否是由 reactive 或 readonly创建的 proxy

isReactive

        检查对象是否是由 reactive创建的响应式代理

        如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;

isReadonly

        检查对象是否是由 readonly 创建的只读代理

toRaw

        返回 reactive 或 readonly 代理的原始对象建议保留对原始对象的持久引用。请谨慎使用)。

shallowReactive

        创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。

shallowReadonly

        创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。

toRefs

如果我们使用ES6的解构语法,对reactive返回的对象进行解构获取值,那么之后无论是修改结构后的变量,还是修改reactive 返回的state对象数据都不再是响应式的:

那么有没有办法让我们解构出来的属性是响应式的呢?

        Vue为我们提供了一个toRefs的函数,可以将reactive返回的对象中的属性都转成ref

        那么我们再次进行结构出来的 name 和 age 本身都是 ref的

 

 这种做法相当于已经在state.name和ref.value之间建立了 链接任何一个修改都会引起另外一个变化

toRef

如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法

//不是说每个地方都会用到,info也是响应式的,取值也可以直接用{{info.name}}
<template>
  <div>
    <h2>{{name}}-{{age}}</h2>
    <button @click="changeAge">修改age</button>
  </div>
</template>

<script>
  import { reactive, toRefs, toRef } from 'vue';

  export default {
    setup() {
      const info = reactive({name: "why", age: 18});
      // 1.toRefs: 将reactive对象中的所有属性都转成ref, 建立链接
      // let { name, age } = toRefs(info);
      // 2.toRef: 对其中一个属性进行转换ref, 建立链接
      let { name } = info;
      let age = toRef(info, "age");

      const changeAge = () => {
        age.value++;
      }

      return {
        name,
        age,
        changeAge
      }
    }
  }
</script>

ref其他的API(了解)

unref

如果我们想要获取一个ref引用中的value,那么也可以通过unref方法

        如果参数是一个 ref,则返回内部值否则返回参数本身

        这是 val = isRef(val) ? val.value : val 的语法糖函数;

isRef

        判断值是否是一个ref对象

shallowRef

        创建一个浅层的ref对象

triggerRef

        手动触发和 shallowRef 相关联的副作用

customRef(用的比较少,写一些第三方库时可能会用到)

创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制

        它需要一个工厂函数,该函数接受 track 和 trigger 函数作为参数;

        并且应该返回一个带有 get 和 set 的对象

这里我们使用一个的案例:

        对双向绑定的属性进行debounce(节流)的操作;

//下面案例为输入框简单防抖案例,输入延时0.3s显示


//hook下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);
      }
    }
  })
}
//customeRef.vue文件

<template>
  <div>
    <input v-model="message"/>
    <h2>{{message}}</h2>
  </div>
</template>

<script>
  import debounceRef from './hook/useDebounceRef';

  export default {
    setup() {
      const message = debounceRef("Hello World");

      return {
        message
      }
    }
  }
</script>

<style scoped>

</style>

computed

在前面我们讲解过计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理

        在前面的Options API中,我们是使用computed选项来完成的;

        在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;

如何使用computed呢?

        方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;

        方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;

<template>
  <div>
    <h2>{{fullName}}</h2>
    <button @click="changeName">修改firstName</button>
  </div>
</template>

<script>
  import { ref, computed } from 'vue';

  export default {
    setup() {
      const firstName = ref("Kobe");
      const lastName = ref("Bryant");

      // 1.用法一: 传入一个getter函数
      // computed的返回值是一个ref对象
      const fullName = computed(() => firstName.value + " " + lastName.value);

      // 2.用法二: 传入一个对象, 对象包含getter/setter
      const fullName = computed({
        get: () => firstName.value + " " + lastName.value,
        set(newValue) {
          const names = newValue.split(" ");
          firstName.value = names[0];
          lastName.value = names[1];
        }
      });

      const changeName = () => {
        // firstName.value = "James"
        fullName.value = "coder why";
      }

      return {
        fullName,
        changeName
      }
    }
  }
</script>

侦听数据的变化

在前面的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行某一些操作。

在Composition API中,我们可以使用watchEffect和watch来完成响应式数据的侦听;

        watchEffect用于自动收集响应式数据的依赖;

        watch需要手动指定侦听的数据源;

 watchEffect

当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect。

我们来看一个案例:

        首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;

        其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;

setup() {
      // watchEffect: 自动收集响应式的依赖
      const name = ref("why");
      const age = ref(18);

      const changeName = () => name.value = "kobe"
      const changeAge = () => age.value++

      watchEffect(() => {
        console.log("name:", name.value, "age:", age.value);
      });

watchEffect的停止侦听

如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。

比如在上面的案例中,我们age达到20的时候就停止侦听:

setup() {
      // watchEffect: 自动收集响应式的依赖
      const name = ref("why");
      const age = ref(18);

      const stop = watchEffect(() => {
        console.log("name:", name.value, "age:", age.value);
      });

      const changeName = () => name.value = "kobe"
      const changeAge = () => {
        age.value++;
        if (age.value > 25) {
          stop();
        }
      }

      return {
        name,
        age,
        changeName,
        changeAge
      }
    }

watchEffect清除副作用

什么是清除副作用呢?

        比如在开发中我们需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候,我们停止了侦听器, 或者侦听器侦听函数被再次执行了。

        那么上一次的网络请求应该被取消掉,这个时候我们就可以清除上一次的副作用;

在我们给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate

        当副作用即将重新执行 或者 侦听器被停止 时会执行该函数传入的回调函数;

        我们可以在传入的回调函数中,执行一些清楚工作;

  setup() {
      // watchEffect: 自动收集响应式的依赖
      const name = ref("why");
      const age = ref(18);

      const stop = watchEffect((onInvalidate) => {
        const timer = setTimeout(() => {
          console.log("网络请求成功~");
        }, 2000)

        // 根据name和age两个变量发送网络请求
        onInvalidate(() => {
          // 在这个函数中清除额外的副作用
          // request.cancel()
          clearTimeout(timer);
          console.log("onInvalidate");
        })
        console.log("name:", name.value, "age:", age.value);
      });

      const changeName = () => name.value = "kobe"
      const changeAge = () => {
        age.value++;
        if (age.value > 25) {
          stop();
        }
      }

      return {
        name,
        age,
        changeName,
        changeAge
      }
    }
  }

★  setup中使用ref

在讲解 watchEffect执行时机之前,我们先补充一个知识:在setup中如何使用ref或者元素或者组件?

        其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;

 调整watchEffect的执行时机

如果我们希望在第一次的时候就打印出来对应的元素呢?

        这个时候我们需要改变副作用函数的执行时机;

        它的默认值是pre,它会在元素 挂载 或者 更新 之前执行;

        所以我们会先打印出来一个空的,当依赖的title发生改变时,就会再次执行一次,打印出元素;

我们可以设置副作用函数的执行时机:

export default {
    setup() {
      const title = ref(null);

      watchEffect(() => {
        console.log(title.value);
      }, {
        flush: "post"
      })

      return {
        title
      }
    }
  }

Watch的使用

watch的API完全等同于组件watch选项的Property:

        watch需要侦听特定的数据源,并在回调函数中执行副作用;

        默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;

与watchEffect的比较,watch允许我们:

        懒执行副作用(第一次不会直接执行);

        更具体的说明当哪些状态发生变化时,触发侦听器的执行;

        访问侦听状态变化前后的值;

侦听单个数据源

watch侦听函数的数据源有两种类型:

        一个getter函数:但是该getter函数必须引用可响应式的对象(比如reactive或者ref);

        直接写入一个可响应式的对象,reactive或者ref(比较常用的是ref);

<template>
  <div>
    <h2 ref="title">{{info.name}}</h2>
    <button @click="changeData">修改数据</button>
  </div>
</template>

<script>
  import { ref, reactive, watch } from 'vue';

  export default {
    setup() {
      const info = reactive({name: "why", age: 18});

      // 1.侦听watch时,传入一个getter函数
      watch(() => info.name, (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      })

      // 2.传入一个可响应式对象: reactive对象/ref对象
      // 情况一: reactive对象获取到的newValue和oldValue本身都是reactive对象
      watch(info, (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      })
      // 如果希望newValue和oldValue是一个普通的对象,对对象进行解构,获取到的就是普通对象
      watch(() => {
        return {...info}
      }, (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      })
      // 情况二: ref对象获取newValue和oldValue是value值的本身
      const name = ref("why");
      watch(name, (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      })

      const changeData = () => {
        info.name = "kobe";
      }

      return {
        changeData,
        info
      }
    }
  }
</script>

侦听多个数据源
 
侦听器还可以使用数组同时侦听多个源:
<template>
  <div>
    <h2 ref="title">{{info.name}}</h2>
    <button @click="changeData">修改数据</button>
  </div>
</template>

<script>
  import { ref, reactive, watch } from 'vue';

  export default {
    setup() {
      // 1.定义可响应式的对象
      const info = reactive({name: "why", age: 18});
      const name = ref("why");

      // 2.侦听器watch
      watch([() => ({...info}), name], ([newInfo, newName], [oldInfo, oldName]) => {
        console.log(newInfo, newName, oldInfo, oldName);
      })


      const changeData = () => {
        info.name = "kobe";
      }

      return {
        changeData,
        info
      }
    }
  }
</script>

侦听响应式对象
如果我们希望侦听一个数组或者对象,那么可以使用一个getter函数,并且对可响应对象进行解构:

 const names = reactive(["aaa", "bbb", "ccc"]);

      watch(()=>[...names],(newValue, oldValue) => {
        console.log(newName);
      })

      const changeArr = () => {
        names.push('ddd');
      }

watch的选项
 

如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:

        也可以传入 immediate 立即执行;

<template>
  <div>
    <h2 ref="title">{{info.name}}</h2>
    <button @click="changeData">修改数据</button>
  </div>
</template>

<script>
  import { ref, reactive, watch } from 'vue';

  export default {
    setup() {
      // 1.定义可响应式的对象
      const info = reactive({
        name: "why", 
        age: 18,
        friend: {
          name: "kobe"
        }
      });

      // 2.侦听器watch
      // () => ({...info})是将info进行ES6的展开运算符进行解构,拿到每个属性后,放到一个新的对象中,这时就不能进行深度监听
      watch(() => ({...info}), (newInfo, oldInfo) => {
        console.log(newInfo, oldInfo);
      }, {
        deep: true,
        immediate: true//默认第一次进来是不会进行上边监听打印的,设为true,第一次运行也会监听打印
      })

      const changeData = () => {
        info.friend.name = "james";
      }

      return {
        changeData,
        info
      }
    }
  }
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值