MST

星途 面试题库

面试题:Vue Composition API中computed与watch的底层原理及定制扩展

深入探讨Vue Composition API中computed和watch的底层实现原理。假设现在需要对computed进行定制扩展,使其支持异步计算并缓存结果,你会如何设计并实现该功能?阐述你的设计思路以及涉及到的关键技术点。
27.7万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

1. Vue Composition API中computed和watch底层实现原理

  • computed
    • Vue 的 computed 本质是依赖收集和缓存机制。当一个 computed 属性被创建时,它会在内部创建一个 Watcher 实例。
    • 这个 Watcher 会收集依赖,也就是在计算该 computed 值过程中所依赖的响应式数据。例如,如果一个 computed 依赖于某个响应式数据 data.a,那么当 data.a 发生变化时,就会触发 computed 的重新计算。
    • 同时,computed 有缓存机制,只有当它所依赖的响应式数据发生变化时,才会重新计算。如果依赖的数据没有变化,多次访问 computed 属性会直接返回缓存的结果,从而提高性能。
  • watch
    • watch 也是基于 Watcher 实现的。它会观察一个或多个响应式数据的变化。
    • 当被观察的数据发生变化时,watch 回调函数就会被执行。watch 可以进行深度观察,通过设置 deep: true 来监听对象内部深层次属性的变化。
    • 此外,watch 还可以在数据变化前(immediate: true)执行回调,常用于初始化时执行某些操作。

2. 设计思路

  • 封装异步计算逻辑:使用 async/await 来处理异步操作,这样可以将异步代码以同步的方式书写,使代码更易读。
  • 缓存设计:维护一个缓存对象,用于存储已经计算过的结果。每次计算前,先检查缓存中是否已经有对应的结果,如果有则直接返回。
  • 依赖收集:沿用 Vue 原有的依赖收集机制,当依赖的数据发生变化时,清除对应的缓存,以便下次重新计算。

3. 关键技术点及实现

  1. 创建异步computed函数
import { ref, effect, ReactiveEffect } from 'vue';

function asyncComputed(getter) {
  const cache = ref();
  let isPending = false;
  let currentEffect: ReactiveEffect | null = null;

  const runner = async () => {
    if (isPending) return;
    isPending = true;
    try {
      const result = await getter();
      cache.value = result;
    } finally {
      isPending = false;
    }
  };

  effect(() => {
    currentEffect = effect;
    runner();
  });

  return {
    get value() {
      if (currentEffect) {
        currentEffect.depend();
      }
      return cache.value;
    }
  };
}
  1. 使用示例
import { reactive } from 'vue';

const data = reactive({
  num: 1
});

const asyncComputedValue = asyncComputed(async () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(data.num * 2);
    }, 1000);
  });
});

// 使用
console.log(asyncComputedValue.value); 

在上述代码中:

  • asyncComputed 函数接收一个异步 getter 函数。
  • cache 用于缓存计算结果。
  • isPending 用于防止在异步操作进行中重复执行。
  • runner 函数执行异步计算,并将结果存入缓存。
  • effect 用于依赖收集,当依赖的数据变化时,重新执行 runner 函数,并清除缓存(这里通过重新执行 runner 实现,因为重新执行会覆盖缓存)。
  • value 属性用于获取计算结果,并进行依赖收集。