ref.spec

接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .value。

  1. const count = ref(0)
  2. console.log(count.value) // 0
  3. count.value++
  4. console.log(count.value) // 1

如果传入 ref 的是一个对象,将调用 reactive 方法进行深层响应转换

更多文档: https://vue3js.cn/vue-composition-api/#reactive

正文

  1. 返回值是一个带有 value 对象, 并且是可以响应的 ```js it(‘should hold a value’, () => { const a = ref(1) expect(a.value).toBe(1) a.value = 2 expect(a.value).toBe(2) })

it(‘should be reactive’, () => { const a = ref(1) let dummy let calls = 0 effect(() => { calls++ dummy = a.value }) expect(calls).toBe(1) expect(dummy).toBe(1) a.value = 2 expect(calls).toBe(2) expect(dummy).toBe(2) // same value should not trigger a.value = 2 expect(calls).toBe(2) expect(dummy).toBe(2) })

  1. 2. 嵌套的属性可以响应
  2. ```js
  3. it('should make nested properties reactive', () => {
  4. const a = ref({
  5. count: 1
  6. })
  7. let dummy
  8. effect(() => {
  9. dummy = a.value.count
  10. })
  11. expect(dummy).toBe(1)
  12. a.value.count = 2
  13. expect(dummy).toBe(2)
  14. })
  1. 传递空值也可以响应

    1. it('should work without initial value', () => {
    2. const a = ref()
    3. let dummy
    4. effect(() => {
    5. dummy = a.value
    6. })
    7. expect(dummy).toBe(undefined)
    8. a.value = 2
    9. expect(dummy).toBe(2)
    10. })
  2. refreactive 中会被转换成原始值,而非 ref

    1. it('should work like a normal property when nested in a reactive object', () => {
    2. const a = ref(1)
    3. const obj = reactive({
    4. a,
    5. b: {
    6. c: a
    7. }
    8. })
    9. let dummy1: number
    10. let dummy2: number
    11. effect(() => {
    12. dummy1 = obj.a
    13. dummy2 = obj.b.c
    14. })
    15. const assertDummiesEqualTo = (val: number) =>
    16. [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
    17. assertDummiesEqualTo(1)
    18. a.value++
    19. assertDummiesEqualTo(2)
    20. obj.a++
    21. assertDummiesEqualTo(3)
    22. obj.b.c++
    23. assertDummiesEqualTo(4)
    24. })
  3. ref 嵌套时会自动 unwrap, 访问 b.value 相当于 b.value.value ```js it(‘should unwrap nested ref in types’, () => { const a = ref(0) const b = ref(a)

    expect(typeof (b.value + 1)).toBe(‘number’) })

it(‘should unwrap nested values in types’, () => { const a = { b: ref(0) }

const c = ref(a)

expect(typeof (c.value.b + 1)).toBe(‘number’) })

it(‘should NOT unwrap ref types nested inside arrays’, () => { const arr = ref([1, ref(1)]).value ;(arr[0] as number)++ ;(arr[1] as Ref).value++

const arr2 = ref([1, new Map(), ref(‘1’)]).value const value = arr2[0] if (isRef(value)) { value + ‘foo’ } else if (typeof value === ‘number’) { value + 1 } else { // should narrow down to Map type // and not contain any Ref type value.has(‘foo’) } })

  1. 6. 会检测传递 `ref` 的值类型 ,如果是引用类型就 `reactive` ,不是直接返回结果
  2. ```js
  3. it('should keep tuple types', () => {
  4. const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
  5. 0,
  6. '1',
  7. { a: 1 },
  8. () => 0,
  9. ref(0)
  10. ]
  11. const tupleRef = ref(tuple)
  12. tupleRef.value[0]++
  13. expect(tupleRef.value[0]).toBe(1)
  14. tupleRef.value[1] += '1'
  15. expect(tupleRef.value[1]).toBe('11')
  16. tupleRef.value[2].a++
  17. expect(tupleRef.value[2].a).toBe(2)
  18. expect(tupleRef.value[3]()).toBe(0)
  19. tupleRef.value[4].value++
  20. expect(tupleRef.value[4].value).toBe(1)
  21. })
  22. it('should keep symbols', () => {
  23. const customSymbol = Symbol()
  24. const obj = {
  25. [Symbol.asyncIterator]: { a: 1 },
  26. [Symbol.unscopables]: { b: '1' },
  27. [customSymbol]: { c: [1, 2, 3] }
  28. }
  29. const objRef = ref(obj)
  30. expect(objRef.value[Symbol.asyncIterator]).toBe(obj[Symbol.asyncIterator])
  31. expect(objRef.value[Symbol.unscopables]).toBe(obj[Symbol.unscopables])
  32. expect(objRef.value[customSymbol]).toStrictEqual(obj[customSymbol])
  33. })
  1. unref 可以将 ref 还原成原始值

    1. test('unref', () => {
    2. expect(unref(1)).toBe(1)
    3. expect(unref(ref(1))).toBe(1)
    4. })
  2. shallowRef 不会发生响应,替换掉整个对象会触发响应 ```js test(‘shallowRef’, () => { const sref = shallowRef({ a: 1 }) expect(isReactive(sref.value)).toBe(false)

    let dummy effect(() => { dummy = sref.value.a }) expect(dummy).toBe(1)

    sref.value = { a: 2 } expect(isReactive(sref.value)).toBe(false) expect(dummy).toBe(2) })

  1. 9. `shallowRef` 可以强制触发更新
  2. ```js
  3. test('shallowRef force trigger', () => {
  4. const sref = shallowRef({ a: 1 })
  5. let dummy
  6. effect(() => {
  7. dummy = sref.value.a
  8. })
  9. expect(dummy).toBe(1)
  10. sref.value.a = 2
  11. expect(dummy).toBe(1) // should not trigger yet
  12. // force trigger
  13. triggerRef(sref)
  14. expect(dummy).toBe(2)
  15. })
  1. isRef 可以检测各种类型是否是 ref

    1. test('isRef', () => {
    2. expect(isRef(ref(1))).toBe(true)
    3. expect(isRef(computed(() => 1))).toBe(true)
    4. expect(isRef(0)).toBe(false)
    5. expect(isRef(1)).toBe(false)
    6. // an object that looks like a ref isn't necessarily a ref
    7. expect(isRef({ value: 0 })).toBe(false)
    8. })
  2. 支持自定义 ref, 自由控制 track, trigger 时间

    1. test('customRef', () => {
    2. let value = 1
    3. let _trigger: () => void
    4. const custom = customRef((track, trigger) => ({
    5. get() {
    6. track()
    7. return value
    8. },
    9. set(newValue: number) {
    10. value = newValue
    11. _trigger = trigger
    12. }
    13. }))
    14. expect(isRef(custom)).toBe(true)
    15. let dummy
    16. effect(() => {
    17. dummy = custom.value
    18. })
    19. expect(dummy).toBe(1)
    20. custom.value = 2
    21. // should not trigger yet
    22. expect(dummy).toBe(1)
    23. _trigger!()
    24. expect(dummy).toBe(2)
    25. })