Vue3 源码解读系列(六)——计算属性

计算属性

计算属性允许用户定义一个计算方法,然后根据一些依赖的响应式数据计算出新值并返回。

当依赖发生变化时,计算属性可以自动重新计算获取新值,使用方便。

计算属性的运行机制:

Vue3 源码解读系列(六)——计算属性_第1张图片

计算属性的两个特点:

  1. 延时计算

    只有当访问计算属性的时候,真正运行 computed getter 函数计算。

  2. 缓存

    内部会缓存上次的计算结果 value,而且只有 dirty 为 true 时才会重新计算,如果访问计算属性时 dirty 为 false,那么直接返回这个 value。

/**
 * computed 的实现
 * 1、标准化参数
 * 2、创建副作用函数
 * 3、创建 computed 对象
 */
function computed(getterOrOptions) {
  let getter // getter 函数
  let setter // setter 函数

  // 1、标准化参数
  if (isFunction(getterOrOptions)) {
    // 表面传入的是 getter 函数,不能修改计算属性的值
    getter = getterOrOptions
    setter = (process.env.NODE_ENV !== 'production') ? () => {
      conosle.warn(/* ... */)
    } : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  let dirty = true // 数据是否脏
  let value // 计算结果
  let computed

  // 2、创建副作用函数
  const runner = effect(getter, {
    lazy: true, // 延时执行
    computed: true, // 标记这是一个 computed effect 用于在 trigger 阶段的优先级排序
    scheduler: () => { // 调度执行的实现 
      if (!dirty) {
        dirty = true
        // 派发通知,通知运行访问该计算属性的 activeEffect
        trigger(computed, "set", 'value') // SET
      }
    }
  })

  // 创建 computed 对象
  computed = {
    __v_isRef: true,
    effect, // 暴露 effect 对象以便计算属性可以停止计算
    runner,
    get value() { // 计算属性的 getter
      // 只有数据为脏的时候才会重新计算
      if (dirty) {
        value = runner()
        dirty = false
      }
      // 依赖收集,收集运行访问该计算属性的 activeEffect
      track(computed, "get", 'value') // GET
      return value
    },
    set value(newValue) { // 计算属性的 setter
      setter(newValue)
    }
  }
  return computed
}

/**
 * trigger 函数内部添加 effects 的过程
 */
const add = (effectsToAdd) => {
  if (effectsToAdd) {
    effectsToAdd.forEach(effect => {
      if (effect !== activeEffect || !shouldTrack) {
        if (effect.options.computed) {
          computedRunners.add(effect)
        } else {
          effects.add(effect)
        }
      }
    })
  }
}

// trigger 函数内部的执行函数
const run = (effect) => {
  if (effect.options.scheduler) { // 调度执行
    effect.options.scheduler(effect)
  } else { // 直接运行
    effect()
  }
}

// 先执行 computed 的调度,再执行普通依赖函数的调度
computedRunners.forEach(run)
effects.forEach(run)

问题:能否在 computed 中执行异步函数?

答:可以,分情况:

  1. 使用 async/await 同步后的异步函数

    由于 async 定义的函数返回的是 promise,所以虽然内部的执行同步了,但是不能达到我们想要的效果,不会报错,但几乎可以视为不行。

  2. 未使用 async/await 同步后的异步函数

    可以正常返回,但是可能会影响用户体验,例如:在回调函数中通过 setTimeout 将某个依赖变量进行更改,此时页面会先展示定时器执行前的结果,等定时器执行完成后,页面会重新渲染。

如果对上述结果有问题的可以去了解 V8 的事件循环机制。

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