原始值指的是Boolean、Number、BigInt、String、Symbol、undefined和null等原始数据类型的值。

引入Ref的概念

这个没有什么好讲的,主要就是基本数据类型的形参与实参之间无法建立联系,所以需要包裹一层进行数据代理。

响应丢失问题

考虑如下代码

export default {
  setup() {
    // 响应式数据
    const obj = reactive({ foo: 1, bar: 2 })

    // 1s 后修改响应式数据的值,不会触发重新渲染
    setTimeout(() => {
      obj.foo = 0
    },)

    return {
      ...obj
    }
  }
}

上面代码中,将响应式数据通过展开符转换为另一个对象,导致响应丢失,此时我们可以通过toReftoRefs来使其保持响应状态

function toRefs(obj) {
  const ret = {}
  // 使用 for...in 循环遍历对象
  for (const key in obj) {
    // 逐个调用 toRef 完成转换
    ret[key] = toRef(obj, key)
  }
  return ret
}

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
}

使用例子

const obj = reactive({ foo: 1, bar: 2 })

const newObj = { ...toRefs(obj) }
console.log(newObj.foo.value) // 1
console.log(newObj.bar.value) // 2

自动脱Ref

自动脱Ref指的是,在模板函数中,我们可以直接使用ref的值来访问, 而不需要访问其value属性。

如下代码,其中count为一个ref值

<p>{{ count }}</p>
function proxyRefs(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const value = Reflect.get(target, key, receiver)
      return value.__v_isRef ? value.value : value
    },
    set(target, key, newValue, receiver) {
      // 通过 target 读取真实值
      const value = target[key]
      // 如果值是 Ref,则设置其对应的 value 属性值
      if (value.__v_isRef) {
        value.value = newValue
        return true
      }
      return Reflect.set(target, key, newValue, receiver)
    }
  })
}