一、ref

普通值变成响应式 就需要使用 ref

原理:将普通类型转化成一个对象,这个对象中有 value 属性, 指向原来的值。

image.png
ref 和 reactive 的区别: reactive 内部采用 proxy,ref 内部使用的是 defineProperty

  1. //将普通类型变成一个对象
  2. export function ref(value?: unknown) {
  3. return createRef(value) // vue 源码基本上都是高阶函数,做了类似柯里化的功能
  4. }
  5. function createRef(rawValue: unknown, shallow = false) {
  6. if (isRef(rawValue)) {
  7. return rawValue
  8. }
  9. // 返回一个类
  10. return new RefImpl(rawValue, shallow)
  11. }
  12. // 如果是对象就调用 reactive 转成响应式的,否则直接返回
  13. const convert = <T extends unknown>(val: T): T =>
  14. isObject(val) ? reactive(val) : val
  15. // 这个类的底层实现就是 defineproperty
  16. class RefImpl<T> {
  17. private _value: T // 声明了才能在下面用,不声明使用会报错 ,声明但没有赋值
  18. public readonly __v_isRef = true
  19. constructor(private _rawValue: T, public readonly _shallow: boolean) { // 参数前面加 private public修饰符 表示此属性放到了实例上
  20. this._value = _shallow ? _rawValue : convert(_rawValue)
  21. }
  22. // 类的属性访问器
  23. get value() {
  24. track(toRaw(this), TrackOpTypes.GET, 'value')
  25. return this._value // 取值取value,会代理到 _value 上
  26. }
  27. set value(newVal) {
  28. // 判断新值和老值是否有变化
  29. if (hasChanged(toRaw(newVal), this._rawValue)) {
  30. this._rawValue = newVal // 新值会作为老值
  31. this._value = this._shallow ? newVal : convert(newVal) // 如果是深度需要把这个对象全部转成响应式的
  32. trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
  33. }
  34. }
  35. }

二、toRef

toRef 将一个对象中的某一个属性转换成 ref

image.png

  1. class ObjectRefImpl<T extends object, K extends keyof T> {
  2. public readonly __v_isRef = true
  3. constructor(private readonly _object: T, private readonly _key: K) {}
  4. get value() {
  5. return this._object[this._key]
  6. }
  7. set value(newVal) {
  8. this._object[this._key] = newVal
  9. }
  10. }
  11. // 将一个 key 对应的值转换成 ref
  12. export function toRef<T extends object, K extends keyof T>(
  13. object: T, // 目标对象
  14. key: K // key 属性
  15. ): ToRef<T[K]> {
  16. return isRef(object[key])
  17. ? object[key]
  18. : (new ObjectRefImpl(object, key) as any)
  19. }

三、toRefs

将一个对象中的多个属性转成 ref
image.png

  1. // 循环调用 toRef
  2. export function toRefs<T extends object>(object: T): ToRefs<T> {
  3. if (__DEV__ && !isProxy(object)) {
  4. console.warn(`toRefs() expects a reactive object but received a plain one.`)
  5. }
  6. const ret: any = isArray(object) ? new Array(object.length) : {}
  7. for (const key in object) {
  8. ret[key] = toRef(object, key)
  9. }
  10. return ret
  11. }

使用场景
image.png

toRef 和 toRefs 相当于响应式解构 ,解构某一个就使用 toRef ,解构多个使用 toRefs。