第五章 手写ref函数

目录

1.ref基本实现

2.toRef基本实现

3.toRefs基本实现

4.proxyRefs基本实现


1.ref基本实现

ref函数接收一个一个值,如果这个值是基础数据类型,则直接返回一个RefImpl对象,并对这个对象设置属性访问器和属性修改器,在其中进行对应的依赖收集和触发更新

如果接受一个复杂类型的数据,则会将数据包装成一个RefImpl对象,并将其value值通过reactive进行代理,最后设置给RefImpl对象。

1. 创建ref.ts 文件,在文件中导出一个函数ref,并在index。ts中统一导出

# ref.ts


export function ref(value) {

    return createRef(value) // 创建一个Ref对象并返回

}

2. 实现createRef函数

function createRef(value) {

    return new RefImpl(value)

}

class RefImpl {

    __v_isRef = true // 标识当前变量是否是ref对象

    _value // 存储ref对象的值

    dep = [] // 用于收集依赖

    constructor(public rawValue) {

        // 判断是否是对象,如果是对象则递归ref

        this._value  = toReactive(rawValue) // 将对对象代理,将普通值原封不动返回

    }

    get value() {

        return this._value // 返回ref对象的值

    }

    set value(newValue) {

        if(newValue !== this._value) {

            this._value = newValue // 更新ref对象的值

            this.rawValue = newValue // 更新ref对象的原始值

        }

    }

}
# reactive.ts

 export function toReactive(value) {

    return isObject(value) ? reactive(value) : value

 }

```

3. 收集依赖并触发更新

  # ref.ts



import { currentEffect, trackEffect, triggerEffect } from "./effect"

import { toReactive } from "./reactive"

import { createDep } from "./reactiveEffect"



  class RefImpl {

    ...

    get value() {

      ...

      trackrRefValue(this) // 收集依赖

      ...

    }

    set value(newValue) {

      ...

      triggerRefValue(this) // 触发依赖

    }

  }



  function trackrRefValue(ref) {

    // 判断当前需要收集的依赖是否存在

    if(currentEffect) {

        // 收集依赖,并创建一个以当前ref为key的依赖表

        trackEffect(currentEffect,ref.dep = createDep(() => ref.dep = undefined,"undefined"))

    }

  }

  function triggerRefValue(ref) {

      // 触发更新

      let dep = ref.dep // 提取当前ref的依赖表

      if(dep) {

          triggerEffect(dep) // 触发依赖表

      }

  }

2.toRef基本实现

在我们书写代码时,有可能会遇到下面这种状况

  const user = reactive({

    name: "zhangsan",

    age: 18

  })


  const { name,age } = user

如上,我们对一个reactive响应式对象进行了结构,这个操作在本质上会破坏proxy对象的代理,让响应式对象失去响应式特性,因此就有了toRef函数,专门用于将reactive对象中的某个属性结构出来并转换成ref对象。

该函数需要接受两个参数,第一个参数是reactive对象,第二个参数是reactive对象的属性key。返回一个ref对象,该对象的value属性就是reactive对象中key对应的值。并且支持响应式

1. 创建toRef函数

  # ref.ts

  export function toRef(reactive,key) {

    return new ObjectRefImpl(reactive,key)

  }

2. 实现ObjectRefImpl类

  class ObjectRefImpl {

    __v_isRef = true // 标识当前变量是否是ref对象

    constructor(public _object, public _key) {



    }

    get value() {

        return this._object[this._key] // 返回对象的值

    }

    set value(newValue) {

        this._object[this._key] = newValue // 更新对象的值

    }

  }

3.toRefs基本实现

toRefs函数用于将reactive对象中的所有属性都转换成ref对象,并且返回一个包含所有ref对象的proxy对象。

1. 创建toRefs函数

  export function toRefs(reactive) {

    const res = {}

    for(const key in reactive) {

        res[key] = toRef(reactive, key)

    }

    return res

  }

4.proxyRefs基本实现

这个api主要用于在模板渲染时将所有的ref对象都代理成Proxy,在每次读取ref对象的属性值时,直接将其的value属性返回,这样在每次使用时就不需要再.value调用了。

1. 创建proxyRefs函数

export function proxyRefs(objectWithRefs) {

    return new Proxy(objectWithRefs, {

        get(target,key,receiver) {

           let r =  Reflect.get(target,key,receiver)

           return r.__v_isRef ? r.value : r // 自动将value去掉包裹

        },

        set(target,key,value,receiver) {

            const oldValue = target[key]



                // 如果oldValue是ref对象,则更新ref对象的值

                if(oldValue && oldValue.__v_isRef) {

                    oldValue.value = value // 更新ref对象的值

                    return true

                } else {

                    // 如果oldValue不是ref对象,则更新对象的值

                    return Reflect.set(target,key,value,receiver) // 更新对象

                 }

            }
    })

}

你可能感兴趣的:(手写Vue3源码,javascript,前端,html)