Vue3 中的 ref
接收一个内部值并返回一个响应式且可变的 ref 对象(RefImpl
的实例对象)。
可以无脑使用 ref 代替 reactive,使用 ref 方便书写,使用 reactive 突出数据的关联性。
JS
中操作数据需要:xxx.value
,但模板中不需要.value
,直接使用即可。
<template>
<div>
ref: {{ man }}
shallowRef: {{ man2 }}
customRef: {{ obj }}
</div>
<button @click="change">click me </button>
</template>
<script setup lang="ts">
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue';
// ref 是深层次响应,shallowRef 是浅层次响应,只能响应到 value 层,即 value 以后响应不到
// 只有 shallowRef 与 ref 的变量同时出现在 template 标签里面才会影响,单独出现不影响。
// 因为 ref 底层调用过 triggerRef,会影响 shallowRef 响应到深层
const man = ref({ name: 'vue2' })
const man2 = shallowRef({ name: 'vue2' })
const change = () => {
// man.value.name = 'vue3'
console.log(isRef(man));// true 判断是不是ref对象
// man2.value.name = 'vue3'
// man2.value = {
// name: 'vue3'
// } 写成这种形式才可以改变name的值,因为可以认为此时修改的是value层级的值
man2.value.name = 'vue3'
triggerRef(man2) //此时也可以改变name
// 这就相当于 ref = shallowRef + triggerRef
console.log(obj);
}
// customRef 是一个用于创建自定义响应式数据的函数
function MyRef<T>(value: T) {
return customRef((track, trigger) => {
return {
// 触发依赖和收集依赖的过程交给我们手动实现
get() {
track() // 追踪触发依赖
return value
},
set(newVal) {
// 收集依赖更新
value = newVal
trigger()
},
}
})
}
const obj = MyRef<string>('vue~')
</script>
<style lang="scss" scoped>
</style>
通过使用
shallowRef()
和shallowReactive()
来绕开深度响应。浅层式API
创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
ref 的 ts 写法
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue';
// 写法一:
// type n = {
// num: number;
// }
// const a = ref<n>({ num: 3})
// 写法二:
// import { Ref } from 'vue';
// type n = {
// num: number;
// }
// const a:Ref<n> = ref({ num: 3})
// 写法三:
// 自行做数据类型推导
const a = ref({ num: 3})
customRef 可以用防抖优化
<template>
<button @click="xxx">修改</button>
<div>{{ obj }}</div>
</template>
<script setup lang="ts">
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue';
function MyRef<T>(value: T) {
let timer: any
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
// 如果按钮会调用接口,一直点按钮则会重复调用接口,造成服务器压力
// console.log("触发了");
// 因此可以手动写定时器防抖
// 清除之前可能存在的定时器 timer
clearTimeout(timer)
// 创建新的定时器
timer = setTimeout(() => {
console.log("触发了");
value = newValue;
timer = null
trigger();
}, 500)
}
}
})
}
const obj = MyRef<string>('customRef 定义')
const xxx = () => {
obj.value = 'customRef 被修改了'
}
</script>
<style scoped></style>
ref 获取 DOM 元素
<template>
<button @click="xxx">渲染DOM后触发</button>
<div ref="dom">这是一个div元素</div>
</template>
<script setup lang="ts">
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue';
const dom = ref<HTMLDivElement>();
// 不可以直接这样直接输出,因为DOM还没有渲染
// console.log(dom.value?.innerText);
// onMounted(() => {
// console.log(dom.value?.innerText);
// }),
const xxx = () => {
console.log(dom.value?.innerText);
}
</script>
<style scoped></style>
Vue2 中的 ref 和 $refs
1.给要获取的盒子添加ref属性
<div ref="chartRef">我是渲染图表的容器</div>
2.获取时通过 $refs获取 this.$refs.chartRef 获取
mounted () {
console.log(this.$refs.chartRef)
}