1. ref
1.1 作用
将数据包装成 RefImpl 对象,实现响应式
1.2 用法
<template>
<div>{{ Man }}</div>
<hr>
<button @click="change">修改</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
type M = {
name: string
}
const Man = ref<M>({name: 'lzy'}) // 返回的是 RefImpl 对象
const change = () => {
Man.value.name = "LZY" // 在 js 中获取值,记得写 .value,在模板中则不用
}
</script>
其中,ref 的类型推断有 3 种方式
// 第一种:自动推断
const Man = ref({name: 'lzy'})
// 第二种:自定义泛型
type M = {
name: string
}
const Man = ref<M>({name: 'lzy'})
// 第三种:利用 vue 内置的 Ref 接口
import { Ref } from 'vue'
type M = {
name: string
}
const Man: Ref<M> = ref({name: 'lzy'})
2. isRef
2.1 作用
判断是否是 RefImpl 对象
2.2 用法
<template>
<div>{{ Man }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { ref, Ref, isRef } from 'vue'
type M = {
name: string
}
const Man: Ref<M> = ref({name: 'lzy'})
const man: object = {name: 'lzy2'}
const change = () => {
console.log(isRef(Man)) // true
console.log(isRef(man)) // false
}
</script>
3. shallowRef
3.1 作用
实现浅层次的响应式
3.2 用法
import { shallowRef } from 'vue'
const Man2 = shallowRef({name: 'lzy2'})
const change = () => {
Man2.value.name = 'LZY2' // 无法改变 Man2 的 name 在视图中的数据,但其值其实是变了的
Man2.value = { name: 'LZY2' } // 可以改变视图数据,说明浅层响应式只能响应 .value 的改变
}
3.3 对比 ref
① ref 是深层次的监测, shallowRef 是浅层次的监测
② 注意不要把对 ref 和 shallowRef 监测数据的修改写在一起
因为对 ref 数据的修改会影响到 shallowRef 对数据的监测(即 shallowRef 也可能变成深层次的了)
具体为什么,下面会讲到
示例
<template>
<div>ref: {{ Man }}</div>
<div>shallowRef: {{ Man2 }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { ref,shallowRef } from 'vue'
const Man = ref({name: 'lzy'})
const Man2 = shallowRef({name: 'lzy2'})
const change = () => {
Man.value.name = "我名字改了"
Man2.value.name = '我被影响了'
}
// 结果为:
// ref: { "name": "我名字改了" }
// shallowRef: { "name": "我被影响了" }
</script>
4. triggerRef
4.1 作用
强制更新我们收集的依赖
上面的 ref 与 shallowRef 值同时更新而 shallowRef 受 ref 影响,就是因为收集的 依赖 ref 的 值更新的时候,内部调用了 triggerRef 函数,导致收集的 依赖 shallowRef 的 值也被强制更新了
4.2 用法
<template>
<div>shallowRef: {{ Man2 }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { shallowRef,triggerRef } from 'vue'
const Man2 = shallowRef({name: 'lzy2'})
const change = () => {
Man2.value.name = '我被影响了'
triggerRef(Man2) // 强制更新 浅层响应式的 深层依赖
}
</script>
5. customRef
5.1 作用
自定义一个 ref 响应式对象
5.2 用法
5.2.1 用在 基本类型 上
<template>
<div>customRef: {{ obj }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { customRef } from 'vue'
function MyRef<T>(value: T){
return customRef((track, trigger) => {
return {
get(){
track() // 收集依赖
return value
},
set(newValue){
console.log("触发了")
value = newValue
trigger() // 触发依赖
}
}
})
}
const obj = MyRef<string>("lzy")
const change = () => {
console.log(obj) // CustomRefImpl {dep: Set(1), __v_isRef: true, _get: ƒ, _set: ƒ}
obj.value = "LZY"
}
</script>
按钮点击前结果:customRef: lzy
按钮点击后结果:customRef: LZY
控制台输出:触发了
说明调用了 set 方法
5.2.1 用在 引用类型 上
<template>
<div>customRef: {{ obj }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { customRef } from 'vue'
function MyRef<T>(value: T){
return customRef((track, trigger) => {
return {
get(){
track() // 收集依赖
return value
},
set(newValue){
console.log("触发了")
value = newValue
console.log(newValue === value)
trigger() // 触发依赖
}
}
})
}
const obj = MyRef<object>({name: 'lzy'})
const change = () => {
obj.value.name = "LZY" // 提示:类型“object”上不存在属性“name”。
console.log(obj.value.name) // LZY
}
</script>
可以看到,当想设置 obj.value.name 时,会产生提示信息。
实际上,obj.value.name 的值在控制台打印出来显示是修改了的,但是页面上的值没有变化
说明并没有调用 set 方法,说明 customRef 内部用的是 shallowRef,只能浅层响应
解决方案
// 方法一:直接更改 value
const change = () => {
obj.value = {name: "LZY"}
}
// 方法二:使用 triggerRef() 强制更新依赖
const change = () => {
obj.value.name = "LZY" // 但是仍然会提示:类型“object”上不存在属性“name”。
triggerRef(obj)
}
5.3 应用场景
从 5.1 可以看到,不断点击按钮,set 会不断触发
但由于 set 逻辑是用户可控的
因此我们可以在 set 里面作防抖处理
<template>
<div>customRef: {{ obj }}</div>
<hr>
<button @click="change">按钮</button>
</template>
<script setup lang="ts">
import { customRef } from 'vue'
function MyRef<T>(value: T) {
let timer: any
return customRef((track, trigger) => {
return {
get() {
track() // 收集依赖
return value
},
set(newValue) {
// 实现防抖
clearTimeout(timer)
timer = setTimeout(() => {
console.log("触发了")
value = newValue
trigger() // 触发依赖
timer = null
}, 500)
}
}
})
}
const obj = MyRef<string>("lzy")
const change = () => {
obj.value = "LZY"
}
</script>
6. ref 用于获取 DOM 元素
6.1 用法
1. 在 DOM 元素上添加 ref 属性
<div ref="dom"></div>
2. 在 setup 中设置同名变量,设为 ref()
import { ref } from 'vue'
let dom = ref()
3. 打印一下
console.log(dom) // Ref<undefined>,此时 DOM 还没挂载,所以为 undefined
onMounted(() => {
console.log(dom) // Ref<div>
})
4. 在 DOM 挂载之后再打印
7. ref 对象查看的小妙招
7.1 设置
1. F12 打开浏览器开发者工具
2. 点击右上角 “设置” 小齿轮
3. “偏好设置” 里面勾选 “启动自定义格式设置工具”
7.2 效果
设置之前打印 ref 对象:RefImpl {dep: Set(1), __v_isRef: true, _get: ƒ, _set: ƒ}
设置之后打印 ref 对象:Ref<"lzy">
也可以用于打印 reactive 对象