响应式 | 简述 ref

72 阅读2分钟

核心方法和实现

1.ref()函数

export function ref(value?: unknown) {
  return createRef(value, false)
}
  • 创建一个响应式引用
    • 内部通过 RefImpl 类实现
    • 可以保障任意类型的值
  1. RefImpl类的核心实现:
class RefImpl<T = any> {
  _value: T
  private _rawValue: T
  dep: Dep = new Dep()

  get value() {
    this.dep.track() // 依赖收集
    return this._value
  }

  set value(newValue) {
    if (hasChanged(newValue, this._rawValue)) {
      this._rawValue = newValue
      this._value = toReactive(newValue)
      this.dep.trigger() // 触发更新
    }
  }
}

setter 函数

  1. 参数和初始处理:
set value(newValue) {
    const oldValue = this._rawValue
  • 函数接收一个新值newValue

  • 首先保存当前的原始值到oldValue

  1. 判断是否需要直接使用新值:
const useDirectValue =
    this[ReactiveFlags.IS_SHALLOW] ||
    isShallow(newValue) ||
    isReadonly(newValue)
  • 在三种情况下会直接使用新值而不做深层响应式转换:

    • 当前ref是浅层ref (shallowRef)

    • 新值本身是浅层响应式对象

    • 新值是只读对象

  1. 值的处理:
newValue = useDirectValue ? newValue : toRaw(newValue)
  • 如果useDirectValue为true,直接使用新值

  • 否则,通过toRaw获取新值的原始值(去除响应式包装)

  1. 更新逻辑:
if (hasChanged(newValue, oldValue)) {
    this._rawValue = newValue
    this._value = useDirectValue ? newValue : toReactive(newValue)
  • 只有当新值和旧值不同时才进行更新

  • 更新_rawValue为新的原始值

  • 更新_value,如果是直接使用就用newValue,否则将其转换为响应式对象

  1. 触发依赖更新:
if (__DEV__) {
    this.dep.trigger({
        target: this,
        type: TriggerOpTypes.SET,
        key: 'value',
        newValue,
        oldValue,
    })
} else {
    this.dep.trigger()
}
  • 在开发环境下,触发依赖时会携带更详细的信息

  • 生产环境下直接触发依赖更新

这个setter的主要作用是:

  • 维护ref的原始值和响应式值

  • 处理浅层响应式的情况

  • 确保值的变化时才触发更新

  • 触发依赖追踪系统进行更新

主要特性

  1. 响应式包装
  • 通过 .value 访问和修改值

  • 自动的依赖收集和更新触发

  • 对象类型会被自动转换为响应式对象

  1. 浅层响应式
  • shallowRef() - 只对顶层属性做响应式转换

  • 适用于大型数据结构的性能优化

  1. 工具函数
  • isRef() - 检查是否是ref对象

  • unref() - 如果是ref则返回内部值,否则返回原值

  • toRefs() - 将响应式对象转换为普通对象,其中每个属性都是对应的ref

  1. 自定义 ref
  • customRef() - 允许自定义依赖收集和触发更新的逻辑

  • 提供更灵活的响应式控制