computed.spec

传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。

  1. const count = ref(1)
  2. const plusOne = computed(() => count.value + 1)
  3. console.log(plusOne.value) // 2
  4. plusOne.value++ // 错误!

或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。 ```js const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 }, })

plusOne.value = 1 console.log(count.value) // 0

  1. 更多文档: [https://vue3js.cn/vue-composition-api/#computed](https://vue3js.cn/vue-composition-api/#computed)
  2. ### 正文
  3. 1. 每次返回的是最新的值
  4. ```js
  5. it('should return updated value', () => {
  6. const value = reactive<{ foo?: number }>({})
  7. const cValue = computed(() => value.foo)
  8. expect(cValue.value).toBe(undefined)
  9. value.foo = 1
  10. expect(cValue.value).toBe(1)
  11. })
  1. 计算属性默认是 lazy 不会立即执行, 取的值未发生变化不会执行

    1. it('should compute lazily', () => {
    2. const value = reactive<{ foo?: number }>({})
    3. const getter = jest.fn(() => value.foo)
    4. const cValue = computed(getter)
    5. // lazy
    6. expect(getter).not.toHaveBeenCalled()
    7. expect(cValue.value).toBe(undefined)
    8. expect(getter).toHaveBeenCalledTimes(1)
    9. // should not compute again
    10. cValue.value
    11. expect(getter).toHaveBeenCalledTimes(1)
    12. // should not compute until needed
    13. value.foo = 1
    14. expect(getter).toHaveBeenCalledTimes(1)
    15. // now it should compute
    16. expect(cValue.value).toBe(1)
    17. expect(getter).toHaveBeenCalledTimes(2)
    18. // should not compute again
    19. cValue.value
    20. expect(getter).toHaveBeenCalledTimes(2)
    21. })
  2. 如果有effect是依赖 computed 结果的,当它改变时,effect 也会执行

    1. it('should trigger effect', () => {
    2. const value = reactive<{ foo?: number }>({})
    3. const cValue = computed(() => value.foo)
    4. let dummy
    5. effect(() => {
    6. dummy = cValue.value
    7. })
    8. expect(dummy).toBe(undefined)
    9. value.foo = 1
    10. expect(dummy).toBe(1)
    11. })
  3. computed 之间可以相互依赖

    1. it('should work when chained', () => {
    2. const value = reactive({ foo: 0 })
    3. const c1 = computed(() => value.foo)
    4. const c2 = computed(() => c1.value + 1)
    5. expect(c2.value).toBe(1)
    6. expect(c1.value).toBe(0)
    7. value.foo++
    8. expect(c2.value).toBe(2)
    9. expect(c1.value).toBe(1)
    10. })
  4. 参照3,4条 ```js it(‘should trigger effect when chained’, () => { const value = reactive({ foo: 0 }) const getter1 = jest.fn(() => value.foo) const getter2 = jest.fn(() => { return c1.value + 1 }) const c1 = computed(getter1) const c2 = computed(getter2)

    let dummy effect(() => { dummy = c2.value }) expect(dummy).toBe(1) expect(getter1).toHaveBeenCalledTimes(1) expect(getter2).toHaveBeenCalledTimes(1) value.foo++ expect(dummy).toBe(2) // should not result in duplicate calls expect(getter1).toHaveBeenCalledTimes(2) expect(getter2).toHaveBeenCalledTimes(2) })

it(‘should trigger effect when chained (mixed invocations)’, () => { const value = reactive({ foo: 0 }) const getter1 = jest.fn(() => value.foo) const getter2 = jest.fn(() => { return c1.value + 1 }) const c1 = computed(getter1) const c2 = computed(getter2)

let dummy effect(() => { dummy = c1.value + c2.value }) expect(dummy).toBe(1)

expect(getter1).toHaveBeenCalledTimes(1) expect(getter2).toHaveBeenCalledTimes(1) value.foo++ expect(dummy).toBe(3) // should not result in duplicate calls expect(getter1).toHaveBeenCalledTimes(2) expect(getter2).toHaveBeenCalledTimes(2) })

  1. 6. `computed` 可以 `stop`, `stop` 后不再响应
  2. ```js
  3. it('should no longer update when stopped', () => {
  4. const value = reactive<{ foo?: number }>({})
  5. const cValue = computed(() => value.foo)
  6. let dummy
  7. effect(() => {
  8. dummy = cValue.value
  9. })
  10. expect(dummy).toBe(undefined)
  11. value.foo = 1
  12. expect(dummy).toBe(1)
  13. stop(cValue.effect)
  14. value.foo = 2
  15. expect(dummy).toBe(1)
  16. })
  1. 支持自定义 setter , setter 会触发 effect ```js it(‘should support setter’, () => { const n = ref(1) const plusOne = computed({ get: () => n.value + 1, set: val => { n.value = val - 1 } })

    expect(plusOne.value).toBe(2) n.value++ expect(plusOne.value).toBe(3)

    plusOne.value = 0 expect(n.value).toBe(-1) })

it(‘should trigger effect w/ setter’, () => { const n = ref(1) const plusOne = computed({ get: () => n.value + 1, set: val => { n.value = val - 1 } })

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

plusOne.value = 0 expect(dummy).toBe(-1) })

  1. 8. 默认是只读对象,修改会抛出错误
  2. ```js
  3. it('should warn if trying to set a readonly computed', () => {
  4. const n = ref(1)
  5. const plusOne = computed(() => n.value + 1)
  6. ;(plusOne as WritableComputedRef<number>).value++ // Type cast to prevent TS from preventing the error
  7. expect(
  8. 'Write operation failed: computed value is readonly'
  9. ).toHaveBeenWarnedLast()
  10. })