ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property.value

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

ref实现原理

ref的原理就是将一个普通值,转化成对象,并且在获取和设置值时可以增加依赖收集和触发更新的功能

function ref(value){
  return createRef(value)
}

function convert(rawValue){
  return isObject(rawValue) ? reactive(rawValue) : rawValue
}

function createRef(value, shallow = false){
  return new RefImpl(value, shallow)
}

class RefImpl{
  _value;
  __v_isRef = true // flag 表示这是一个ref
  constructor(rawValue,shallow){
    this.rawValue = rawValue;
    this.shallow = shallow;
    this._value = shallow ? rawValue : convert(rawValue)
  }

  get value(){
    // 取值的时候收集依赖
    track(this, 'get', 'value')
    return this._value
  }

  set(newValue){
    if(hasChange(newValue, this.rawValue)){
      this.rawValue = newValue;
      this._value = this.shallow ? newValue : convert(newValue)

      // 设置值的时候触发依赖更新
      trigger(this, 'set', 'value', newValue)
    }
  }
}

unref

如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数

function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // unwrapped 现在一定是数字类型
}

toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

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

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

当你要将 propref 传递给复合函数时,toRef 很有用:

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}

即使源 property 不存在,toRef 也会返回一个可用的 ref。这使得它在使用可选 prop 时特别有用,可选 prop 并不会被 toRefs 处理。

toRef实现原理

toRef 只能将响应式对象的某个属性转换为ref,普通对象,没有响应式

class ObjectRefImpl {
    __v_isRef = true;
    constructor(target, key) {
      this.target = target;
      this.key = key
    }
    get value() {
        return this.target[this.key];
    }
    set value(newValue) {
        this.target[this.key] = newValue
    }
}

function toRef(target, key) {
    return isRef(object[key]) ? object[key] : new ObjectRefImpl(target, key)
}

toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 propertyref

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

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
function toRefs() {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret: any = isArray(object) ? new Array(object.length) : {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}