往期内容:
《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.fooconst 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 = _objthis._key = _keythis.__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 = _objectthis._key = _keythis._defaultValue = _defaultValuethis.__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>