《Vue进阶教程》第三十三课:toRef的使用
往期内容:
《Vue进阶教程》第二十二课:自定义更新(调度器)
《Vue进阶教程》第二十三课:渲染计算属性的结果
《Vue进阶教程》第二十四课:优化
《Vue进阶教程》第二十五课:watch基本概念
《Vue进阶教程》第二十六课:实现侦听函数
《Vue进阶教程》第二十七课:实现侦听对象
《Vue进阶教程》第二十八课:实现新旧值
《Vue进阶教程》第二十九课:立即执行的回调
《Vue进阶教程》第三十课:watchEffect
《Vue进阶教程》第三十一课:ref的初步实现
《Vue进阶教程》第三十二课:ref响应丢失问题
1) 基本使用
为了解决在赋值过程中响应丢失问题, Vue3提供了两个API
●toRef: 解决赋值问题
●toRefs: 解决展开, 解构问题
使用演示
toRef的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3.2.41/dist/vue.global.js"></script>
</head>
<body>
<script>
const { reactive, effect, toRef, toRefs } = Vue
// obj是reactive创建的响应式数据(proxy代理对象)
const obj = reactive({ foo: 1, bar: 2 })
effect(() => {
console.log('obj.foo具有响应性:', obj.foo)
})
// 使用toRef定义, 取代基本赋值操作 foo = obj.foo
const foo = toRef(obj, 'foo')
effect(() => {
console.log('foo.value具有响应性:', foo.value)
})
</script>
</body>
</html>
2) toRef的实现
基本实现
function toRef(obj, key) {
const wrapper = {
get value() {
return obj[key]
},
set value(val) {
obj[key] = val
},
}
Object.defineProperty(wrapper, '__v_isRef', {
value: true,
})
return wrapper
}
在Vue3中, 将wrapper抽象成了ObjectRefImpl类的实例, 大致的实现如下
reactive.js
class ObjectRefImpl {
constructor(_obj, _key) {
this._obj = _obj
this._key = _key
this.__v_isRef = true
}
get value() {
return this._obj[this._key]
}
set value(newVal) {
this._obj[this._key] = newVal
}
}
function toRef(obj, key) {
return new ObjectRefImpl(obj, key)
}
源码解读
- 源码中的
toRef
实现了默认值的功能 - 源码中的
toRef
对要转换的数据做了判断, 如果已经是ref
类型就直接返回
class ObjectRefImpl {
// 支持默认值
constructor(_object, _key, _defaultValue) {
this._object = _object
this._key = _key
this._defaultValue = _defaultValue
this.__v_isRef = true
}
get value() {
const val = this._object[this._key]
return val === undefined ? this._defaultValue : val
}
set value(newVal) {
this._object[this._key] = newVal
}
}
// 1. 支持默认值
function toRef(object, key, defaultValue) {
const val = object[key]
// 2. 如果要转换的对象已经是ref类型, 直接返回
// eg: state = reactive({foo: ref(1)}) state.foo已经是ref类型, 直接返回ref(1)
return isRef(val) ? val : new ObjectRefImpl(object, key, defaultValue)
}
测试用例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./reactive.js"></script>
</head>
<body>
<script>
// obj是响应式数据
const obj = reactive({ foo: 1, bar: 2 })
const foo = toRef(obj, 'foo')
effect(() => {
console.log('foo.value具备响应性:', foo.value)
})
</script>
</body>
</html>