浅谈Vue3 computed计算属性

什么是computed

官方给出的解释:接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象

// 只读
function computed<T>(
  getter: () => T,
  debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>

// 可写的
function computed<T>(
  options: {
    get: () => T
    set: (value: T) => void
  },
  debuggerOptions?: DebuggerOptions
): Ref<T>

从代码上看这么解释毫无破绽,甚至于说人尽可知。但是很少有人知道computed或者想了解其真正有用的几大特性

computed实用特性

1. 依赖追踪

import { reactive, computed } from 'vue'

const state = reactive({
  s1: 1,
  s2: 2
})
const info = computed(() => {
  return state.a + state.b
})

我们定义了一个响应式数据state和一个计算属性info , Vue会自动追踪info 依赖的数据state.s1和state.s2,并建立相应的依赖关系。
也就是只有state.s1和state.s2发生变化的时候,info 才会重新计算,info 都将丝毫不受影响。

2. 缓存
还是上面的例子,如果state.s1和state.s2一直都不再改变值了,那么我们读取info 的时候,它将会返回上一次计算的结果,而不是重新计算。

3. 懒计算

这个特性比较容易被忽略,简单地说只有计算属性真正被使用(读取)的时候才会进行计算,否则咱就仅仅是定义了一个变量而已。

 import { reactive, ref, toRefs , onMounted,watch,computed } from 'vue';

  const state = reactive({
    s1: 1,
    s2: 2
  })

  const info = computed(() => {
    console.log('初始执行计算')
    return state.s1+ state.s2
  })
  
  const handleEdit=(type)=>{
    console.log('step1', info.value)
    state.s1 = 3
    setTimeout(() => {
      // 而是要等到再次读取的时候才会触发重新计算
      console.log('step2', info.value)
    }, 100)
  }

在这里插入图片描述
了解完以上特性后我们回归到问题的本质,尝试构造一个符合以上特性的函数

1. 懒计算
依赖 effect 构造 (effect注册的回调都是立刻执行)

  const info = computed(() => {
    console.log('初始执行计算')
    return state.s1+ state.s2
  })

  //一个基础的effect
  const infoEffect = effect(() => {
    console.log('初始执行计算')// 立刻被打印
    const back = state.s1+ state.s2
    return back
  })
  
  const handleEdit=()=>{
    console.log(infoEffect)
  }

想要实现computed的懒执行,添加一个额外的参数lazy。它要实现的功能是:如果传递了lazy为true,副作用函数将不会立即执行,而是将执行的时机交还给用户,由用户决定啥时候执行
浅谈Vue3 computed计算属性_第1张图片

  //一个基础的effect
  const infoEffect = effect((fn, options = {}) => {
    const effectFn = () => {
      // ... 业务流
      // 新增加的res存储fn执行的结果
      const res = fn()
      // ... 业务流,返回结果
      return res
    }
    // 只有lazy不为true时才会立即执行
    if (!options.lazy) {
      effectFn()
    }
    //返回副作用函数让用户执行
    return effectFn
  })

改造一下

  //一个基础的effect
  const infoEffect = effect(() => {
    console.log('初始执行计算')// 立刻被打印
    const back = state.s1+ state.s2
    return back
  },
    {
      lazy: true
    }
  )
  const handleEdit=()=>{
    console.log(infoEffect())
  }

在这里插入图片描述
2. 依赖追踪

  function computed(getter){
    
    const effectFn = effect(getter, {
      lazy: true,
    })

    const info = {
      get value () {
        return effectFn()
      }
    }

    return info
  }

  const diInfo=computed(()=>{
    console.log('初始执行计算')
    return state.s1 + state.s2
  })
  
  const handleEdit=()=>{
    console.log(diInfo.value);
    state.s2 = 2
    console.log(diInfo.value)
  }

在这里插入图片描述
浅谈Vue3 computed计算属性_第2张图片
看似没有什么问题,但是这违背了computed的缓存特性

1.只有当其依赖的东西发生变化了才需要重新计算
2.否则就返回上一次执行的结果。

为了达成这个效果我们还需要进一步优化一下

  const computed=()=>{
    const effectFn = effect(getter, {
      lazy: true,
      // 数据发生变化后,不执行注册的回调,而是执行scheduler
      scheduler () {
        // 数据发生了变化后,则重新设置为dirty,那么下次就会重新计算
        dirty = true
      }
    })
    let value
    let dirty = true

    const info = {
      get value () {
        // 2. 只有数据发生变化了才去重新计算
        if (dirty) {
          value = effectFn()
          dirty = false
        }

        return value
      }
    }

    return info
  }
scheduler 任务调度的强大,不仅仅可以实现数组的异步批量更新、在computed和watch中也是必不可少的。
  const diInfo=computed(()=>{
    console.log('初始执行计算')
    return state.s1 + state.s2
  })
  
  const handleEdit=()=>{
    console.log(diInfo.value);
    state.s2=4
    console.log(diInfo.value)
  }

浅谈Vue3 computed计算属性_第3张图片
经过以上的各种操作你是否已经对computed有了一定了解呢

你可能感兴趣的:(Vue,js,前端,vue.js)