vue3响应式API
响应式api
响应式api相关知识复习。
ref()
这个api会返回一个代理对象,通过.value
去获取值。该api的设计主要是为了弥补reactive()
无法代理数字、字符串类型。当传入一个对象的时候,ref内部实际上调用的是reactive()这个api。
- ref嵌套场景
1 | import {ref, shallowRef} from "vue" |
当存在嵌套的时候,使用vue会自动把内层的ref进行解包,然后把其转换成响应式的。所以deepRef的定义相当于这么定义
1 | const deepRef = ref({a: {b: 2}, c: "c"}) |
shallowRef
只会对其顶层属性进行响应式转换,而不会对嵌套的对象进行深层次的响应式处理。
computed()
当我们需要使用已有的状态去去描述其他的状态的时候,通常使用该api。
该api会自动处理计算状态与已有状态的依赖关系。
官方给出的定义
1 | // 只读 |
- 使用
1 | import {computed, ref} from "vue" |
再computed中,getter回调中应该只做相关属性的计算,不应该产生其他的副作用,比如 修改dom
、 做异步请求
等。从上面的例子可以很好的体会到。
因为这个api的设计理念就是提供给我们做实时性的数据计算的。并且计算出来的值再getter函数执行的时候就已经被确定了
,定时器里的内容会被放到宏队列中等待。
等待2s之后,a的值++。 所以一般不要
在getter中做产生副作用的操作。一般会使用侦听器watch
watchEffct
去做。
第二个参数可以设置一个setter回调,这样意味着这个计算属性是可以更改的,如果没有设置setter回调,当我们去更改的时候,控制台会报一个
[Vue warn] Write operation failed: computed value is readonly 的警告
并且vue并不会去更改它的值。
- 使用
1 | const count = ref(1) |
reactive()
reactive()会返回一个对象的响应式代理,和ref()差不多,但是有部分的点是不一样的。
比如对于数组、Map
这样的对象,响应式ref嵌套的时候,ref()会自动解包,但是reactive不会。reactive不能代理基本类型(
数字、字符串等)
1 | import {reactive, ref} from "vue" |
当我们强制使用reactive()去代理一个基本类型的时候,reactive()不会给我们返回一个响应式对象,而是将其原原本本的返回回来,并且会在控制台中输出警告。
1 | import {isReactive, reactive, ref} from "vue" |
避免深层次的响应式转换,与ref(),类似,也有相同的浅层转换api,shallowReactive()
,不过多赘述。
readonly()
这个api将接收一个对象,可以是响应式的或者是普通的原生对象,会返回一个只读
的响应式
副本。
1 | import {isReadonly, readonly} from "vue"; |
watchEffect
这个api接收一个副作用函数,这个副作用函数会立即执行
一次,watchEffect()会自动跟踪相关的依赖项,并且当依赖项状态变化的时候,会重新执行
1 | import {watchEffect, ref} from "vue"; |
副作用函数也可以接收一个参数,这个参数是一个函数类型,当依赖项变化的时候才会执行该函数,这个函数的参数也是一个函数类型。这里稍微有点绕
但是通过官方给出的类型定义就可以理解了
1 | function watchEffect( |
- 使用
1 | import {watchEffect, ref} from "vue"; |
必须要明确一点的是,watch系列的api都是异步的,这样说还是比较模糊,也就是当依赖项改变的时候,会产生一个微队列任务。所以当a.value++
的时候,并不会立即执行副作用函数,而是先去输出 啊啊啊
当不再需要这个侦听器的时候,可以通过watchEffect()的返回值将它销毁掉。一般不怎么会使用到
1 | const stop = watchEffect(() => { |
watch()
该api可以侦听一个或者多个响应式状态
,并且在状态变化
的时候执行回调函数。
也就是说,可以指定侦听的数据源,相较于watchEffect()来说,在代码上比较直观,因为可以在传递参数部分就可以知道监听的是哪个状态。
并且可以在回调函数中访问到变化之前的值
。同样的
,当数据变化的时候会创建一个微队列任务。
区别于watchEffect(),回调函数不会立即执行,只会在所依赖的状态改变的时候才会执行
- 使用
1 | import {reactive, watch} from "vue"; |
第一个参数的类型可以是以下几种类型
- 一个函数,这个函数要返回一个值
- 一个ref
- 一个响应式对象
- 由以上类型的值组成的数组
但是有时候总会有一些奇奇怪怪的写法
1 | import {reactive, ref, watch} from "vue"; |
这样写回调里面的内容是不会触发的,因虽然传递了一个getter()
函数,但是vue不知道你要监控的是这个对象里面的哪一个属性,所以这样写是感知不到的。但是直接把这个回调去掉又会出现一个奇奇怪怪的问题。
1 | import {reactive, ref, watch} from "vue"; |
函数是执行了,但是输出的东西似乎和直觉上不一样,不应该是这样的吗?
New Value: { num: 2, age: 15 }
Old Value: { num: 1, age: 15 }
在内部处理的时候,因为监控的是一个对象,newValue和oldValue实际上指向的是同一个引用,在回调中加上这一行代码
console.log(newValue === oldValue)
你会惊奇的发现输出的结果是true,这就证明了newValue与oldValue指向的是同一个引用。在开发中要稍微注意一下。