第二十九节——组合式API定义响应式数据

ref本质也是reactive,ref(obj)等价于reactive({value: obj})

vue3中实现响应式数据的方法是就是使用ref和reactive,所谓响应式就是界面和数据同步,能实现实时更新

vue2中响应式是通过defineProperty实现的,vue3中是通过ES6的Proxy来实现的

一、reactive

1、概念

reactive的参数必须是一个对象,包括json数据和数组都可以,否则不具有响应式

如果给reactive传递了其他对象(如时间对象),默认情况下修改对象界面不会自动更新,如果想更新,可以通过给对象重新赋值来解决。

2、使用

复制代码
<template>
  <div>
    {{ info.name }} -- {{ info.age }}
    <input type="text" onChange="inputChange" />
  </div>
</template>
<script setup>
// reactive属于vue3 新增的组合式api之一 需要从vue中引入
import { reactive } from "vue";

/**
 * 使用 reactive 定义 响应式数据
 * info 改变数据会重新渲染
 *
 * 注意:不能把整个info的值替换
 */
const info = reactive({ name: "张三", age: 18 });

/**
 *
 * 每次输入框变化,修改info对象里面的name属性
 */
const inputChange = (event) => {
  /**
   * 修改后页面会重新渲染
   * 注意:这里的数据流向和之前一致
   * 数据修改是同步,视图渲染是异步
   */
  info.name = event.target.value;
};
</script>

二、ref(实战用的较多)

1、概念

ref的参数可以是基本数据类型,也可以是引用数据类型。ref会把参数加工成一个响应式对象。如果使用的是基本类型响应式依赖Object.defineProperty( ),如果ref使用的是引用类型,底层ref会借助reactive的proxy 定义响应式。

2、基本使用

复制代码
<template>
  <div>
    <!-- 在template里 可以直接使用ref定义的数据 -->
    {{ num }}
    <button @click="add">+1</button>
  </div>
</template>
<script setup>
import { ref } from "vue";

/**
 * 使用 ref 对象把 传入的基本数据类型包装成一个对象
 * 
 * 返回的num的值 是一个对象
 */
const num = ref(1);

const add = () => {
  /**
   * ref 的值 挂在在对象的value属性
   * 
   * 注意:不能整个修改num的值
   */
  num.value += 1
}


</script>

ref和reactive都属于递归监听,也就是数据的每一层都是响应式的,如果数据量比较大,非常消耗性能,非递归监听只会监听数据的第一层

三、shallowRef和shallowReactive

ref和reactive定义的数据每一层都是响应式数据,使用shallowRef和shallowReactive后只有第一层数据具备响应式。语法和ref和reactive一致

1、shallowRef+triggerRef

复制代码
<template>
  <div>
    {{ infos.name }}

    <button @click="updateChildName">修改子集</button>
    {{ num }} 
    <button @click="add">+1</button>
  </div>
</template>
<script setup>
import { shallowRef, triggerRef } from "vue";

const infos = shallowRef({
  name: "张三",
});

const num = shallowRef(1);

const updateChildName = () => {
  // 这一行视图不会改变
  infos.value.name = "李四";
  /**
   * 如果用了shallowRef 还想让界面刷新可以使用
   * 传入要更新的数据
   * 🤔 看场景使用,一般情况下没有太大必要
   */
  triggerRef(infos)
};

const add = () => {
  // 可以正常触发
  num.value += 1
}

</script>

2、shallowReactive

注意:shallowReactive没有类似triggerRef()的方法

复制代码
<template>
  <div>
    {{ infos.name }} -- {{ infos.child.name  }}

    <button @click="updateChildName">修改第一层</button>
    {{ num }}
    <button @click="updateChildName2">修改第二层</button>
  </div>
</template>
<script setup>
import { shallowReactive } from "vue";

const infos = shallowReactive({
  name: "张三",
  child: {
    name: "张三的儿子",
  },
});

const updateChildName = () => {
  // 正常改变
  infos.name = "李四";
};
const updateChildName2 = () => {
  // 无法修改
  infos.child.name = "李四的儿子";
};
</script>

三、toRaw

1、介绍

有些时候我们不希望数据进行响应式实时更新,可以通过toRaw获取ref或reactive引用的原始数据,通过修改原始数据,不会造成界面的更新,只有通过修改ref和reactive包装后的数据时才会发生界面响应式变化。

2、使用

复制代码
<template>
  <div>
    <button @click="update">+1</button>
    {{ info.name }}
    <button @click="rawUpdate">取消响应式修改</button>
  </div>
</template>
<script setup>
import { reactive, toRaw } from "vue";

const info = reactive({
  name: "张三",
});

const update = () => {
  info.name = info.name + 1;
};

const rawUpdate = () => {
  /**
   * 取消响应式
   * 注意:toRaw 只能用作引用数据类型
   */
  const a = toRaw(info);
  
  // 后续修改失去作用
  a.name = '李四'
};
</script>

四、markRaw(了解)

markRaw包装后的数据永远不会被追踪。暂时没发现有啥用,看个热闹即可

五、toRef

toRef 是对定义的响应对象的某个属性进行引用

复制代码
<template>
  <div>
    {{ info.name }} --- {{ info.age }}
    <button @click="updateName">修改名字</button>
  </div>
</template>
<script setup>
import { ref, toRef } from "vue";

const info = {
  name: "张三",
  age: 18,
};
/**
 * 从info对象中取出name属性
 * 并加上响应式
 * 
 * 返回的nameRef 的 value属性的值 就是 name的值
 */
const nameRef = toRef(info, "name");


const updateName = () => {
  // 把name改成李四
  nameRef.value = '李四'
}

</script>

六、toRefs

遍历对象中的所有属性,将其变为响应式数据,这是因为toRef只能传一个key,toRefs所达到的效果与toRef一样

以上最常用的是ref 和 reactive 其它 看个热闹即可

复制代码
const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// 这个 ref 和源属性已经"链接上了"
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
相关推荐
前端风云志4 分钟前
TypeScript结构化类型初探
javascript
musk121221 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘1 小时前
js代码09
开发语言·javascript·ecmascript
万少1 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL1 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿2 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端