面试题答案
一键面试1. Vue Composition API中computed和watch底层实现原理
- computed:
- Vue 的
computed
本质是依赖收集和缓存机制。当一个computed
属性被创建时,它会在内部创建一个Watcher
实例。 - 这个
Watcher
会收集依赖,也就是在计算该computed
值过程中所依赖的响应式数据。例如,如果一个computed
依赖于某个响应式数据data.a
,那么当data.a
发生变化时,就会触发computed
的重新计算。 - 同时,
computed
有缓存机制,只有当它所依赖的响应式数据发生变化时,才会重新计算。如果依赖的数据没有变化,多次访问computed
属性会直接返回缓存的结果,从而提高性能。
- Vue 的
- watch:
watch
也是基于Watcher
实现的。它会观察一个或多个响应式数据的变化。- 当被观察的数据发生变化时,
watch
回调函数就会被执行。watch
可以进行深度观察,通过设置deep: true
来监听对象内部深层次属性的变化。 - 此外,
watch
还可以在数据变化前(immediate: true
)执行回调,常用于初始化时执行某些操作。
2. 设计思路
- 封装异步计算逻辑:使用
async
/await
来处理异步操作,这样可以将异步代码以同步的方式书写,使代码更易读。 - 缓存设计:维护一个缓存对象,用于存储已经计算过的结果。每次计算前,先检查缓存中是否已经有对应的结果,如果有则直接返回。
- 依赖收集:沿用 Vue 原有的依赖收集机制,当依赖的数据发生变化时,清除对应的缓存,以便下次重新计算。
3. 关键技术点及实现
- 创建异步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;
}
};
}
- 使用示例:
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
属性用于获取计算结果,并进行依赖收集。