计算属性
计算属性允许用户定义一个计算方法,然后根据一些依赖的响应式数据计算出新值并返回。
当依赖发生变化时,计算属性可以自动重新计算获取新值,使用方便。
计算属性的运行机制:
计算属性的两个特点:
-
延时计算
只有当访问计算属性的时候,真正运行 computed getter 函数计算。
-
缓存
内部会缓存上次的计算结果 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 中执行异步函数?
答:可以,分情况:
使用
async/await
同步后的异步函数由于 async 定义的函数返回的是 promise,所以虽然内部的执行同步了,但是不能达到我们想要的效果,不会报错,但几乎可以视为不行。
未使用
async/await
同步后的异步函数可以正常返回,但是可能会影响用户体验,例如:在回调函数中通过 setTimeout 将某个依赖变量进行更改,此时页面会先展示定时器执行前的结果,等定时器执行完成后,页面会重新渲染。
如果对上述结果有问题的可以去了解 V8 的事件循环机制。