setup 中响应式数据的两种实现方法 & reactive API, ref,ReadOnly

 响应式数据的两种实现方法 

注意 传递给子组件父组件等的数据,也需要是响应式的才能联动。否则就一直是初始值。

Reactive API

<h2>当前计数: {{state.counter}}</h2>
<button @click="increment">+1</button>

 import { reactive } from 'vue';

  export default {
    setup() {
      const state = reactive({
        counter: 100
      }) // Reactive API 实现响应式数据

      // 局部函数
      const increment = () => {
        state.counter++;
      }

      return {
        state,
        increment
      }
    }
  }

//事实上,我们vue2编写的data选项,也是在内部交给了reactive函数将其编程响应式对象的
reactive API 对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:
如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;
这个时候Vue3给我们提供了另外一个API:ref AP

Ref API

{{counter}}
{{aa.name}}
//template 里为何不勇加 .value? 在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过 ref.value 的方式
 

import { ref } from 'vue';

setup() {
      let counter = ref(100);
      let aa = ref({counter:100});

      // 局部函数
      const increment = () => {
        counter.value++; 
        aa.value.counter++;
        //setup 函数内部,它依然是一个 ref 引用, 所以对其进行操作时,我们依然需要使用 ref.value的方式;

      }

      return {
        counter,
        increment
      }

Readonly

我们通过reactive或者ref可以获取到一个响应式的对象,但是某些情况下,我们传入给其他地方(组件)的这个 响应式对象希望在另外一个地方(组件)被使用,但是不能被修改,这个时候如何防止这种情况的出现呢?

Vue3为我们提供了readonly的方法;

readonly会返回原生对象的只读代理(也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不 能对其进行修改);

在开发中常见的readonly方法会传入三个类型的参数:

类型一:普通对象;

类型二:reactive返回的对象;

类型三:ref的对象;

import { reactive, ref, readonly } from 'vue';
// 1.普通对象
      const info1 = {name: "why"};
      const readonlyInfo1 = readonly(info1);

      // 2.响应式的对象reactive
      const info2 = reactive({
        name: "why"
      })
      const readonlyInfo2 = readonly(info2);

      // 3.响应式的对象ref
      const info3 = ref("why");
      const readonlyInfo3 = readonly(info3);

readonly 使用规则

readonly返回的对象都是不允许修改的;

但是经过readonly处理的原来的对象是允许被修改的;

  • 比如 const info = readonly(obj),readonly返回的对象info对象是不允许被修改的;
  • 当obj被修改时,readonly返回的info对象也会被修改;

其实本质上就是readonly返回的对象的setter方法被劫持了而已;

Readonly使用场景

在我们传递给其他组件数据时,往往希望其他组件使用我们传递的内容,但是不允许它们修改时,就可以使用 readonly了

<子组件 :info="info" />

给子组件传的值是响应式的时候,值变的时候子组件才能跟着变。 

Reactive判断的API

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

isReactive
检查对象是否是由 reactive创建的响应式代理:
如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;

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

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

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

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

toRefs & toRef

对 reactive 对象进行解构,使解构出来的数据依旧是响应式的。

import { reactive, toRefs, toRef } from 'vue';

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

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

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

      return {
        name,
        age
      }
    }
  }

ref其他的API

unref

  • 如果我们想要获取一个ref引用中的value,那么也可以通过unref方法:
  • 如果参数是一个 ref,则返回内部值,否则返回参数本身;
  • 这是 val = isRef(val) ? val.value : val 的语法糖函数;

isRef
判断值是否是一个ref对象。

shallowRef
创建一个浅层的ref对象;

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

import { ref, shallowRef, triggerRef } from 'vue';

  export default {
    setup() {
      const info = shallowRef({name: "why"})

      const changeInfo = () => {
        info.value.name = "james"; //这个修改不是响应式的,因为 info 是浅层的 ref 对象
        triggerRef(info); //手动触发改变
      }

      return {
        info,
        changeInfo
      }
    }
  }

customRef

n创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制: 它需要一个工厂函数,该函数接受 track 和 trigger 函数作为参数; 并且应该返回一个带有 get 和 set 的对象;

案例: 对双向绑定的属性进行debounce(节流)的操作 (快速在文本框输入文字的时候不要马上响应式的变化)

<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>

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);
      }
    }
  })
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值