我正在参加「掘金·启航计划」
前言: 之前
Vue2的写法用得比较多,然后有次在写Vue3项目时碰到了ref,当时是完全不懂,所以决定在后面把它的用法做个总结。
一、Refs(响应式API)
相信大家对于 Vue3 的 ref 响应式肯定很熟悉,对于基本数据类型,他还是同 Vue2 一样,是靠 Object.defineProperty() 的 get 与 set 方法完成的;但是对于对象类型的数据,则是利用 Vue3 中的一个新的函数—— reactive 函数。
1、常见用法:
let name = ref("张三"); //创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
- JS中操作数据
name.value - 模板中读取数据
<div>{{ name }}</div>
2、unref
接收一个参数,若参数为ref类型则返回内部的值,否则返回参数本身。用法类似于:val = isRef(val) ? val.value : val。
let val = unref(val)
3、toRef
用来为源响应式对象上的 property 新创建一个 ref。然后可以将 ref 传递出去,从而保持对其源 property 的响应式连接。将 prop 的 ref 传递给复合函数时,toRef 很有用。
props: ["person"],
setup(props) {
let newPerson = toRef(props, "person");
}
4、toRefs
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref。
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
注意: toRef 能将对象中的某个属性转换为 ref 类型,而 toRefs 是将整个对象转换为 ref 类型,并且转换和被转换的数据之间类似于浅拷贝,任何一方的改变都会影响另一方的改变。若想双方互不影响,可以直接使用 ref 创建一个新的数据并赋默认值。
5、isRef
用来判断是否是 ref 类型的数据let flag = isRef(参数)。
6、customRef
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并应返回一个带有 get 和 set 的对象。
- 使用
v-model使用自定义 ref 实现debounce的示例:
<input v-model="text" />
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello')
}
}
}
7、shallowRef
对于基本类型的数据是响应式的,对象类型的数据则不是响应式。
<div>爱好:{{ hobby }}</div>
<div>姓名:{{ person.name}}</div>
<div>年龄:{{ person.age}}</div>
let hobby = shallowRef("游泳") //当hobby的值发生改变时,页面也会发生变化
let person = shallowRef({name: "张三", age: 18}) //当person对象内的数据发生改变时,页面不会发生变化
8、triggerRef
手动执行与 shallowRef 关联的任何效果。
const shallow = shallowRef({
greet: 'Hello, world'
})
// 第一次运行时记录一次 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
shallow.value.greet = 'Hello, universe' // 这不会触发,因为 ref 是浅层的
triggerRef(shallow) // 记录 "Hello, universe"
二、ref在标签中的使用
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
<!-- vm.$refs.p 会是 DOM 节点 -->
<p ref="p">hello</p>
<!-- vm.$refs.child 会是子组件实例 -->
<child-component ref="child"></child-component>
<!-- 当动态绑定时,我们可以将ref定义为回调函数,显式地传递元素或组件实例 -->
<child-component :ref="(el) => child = el"></child-component>
当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也是非响应式的,因此你不应该试图用它在模板中做数据绑定。
我遇到的问题:
<child-component ref="child"></child-component>
let child = ref(null)
child.value.getFun() //不理解child上为何有getFun这个函数
原因:这其实就是 Vue2 中的 this.$refs.child.getFun() ,或 this.$refs["child"].getFun() ,
getFun()是child组件里的一个方法, 这里是在父组件中调用子组件的方法。