微信小程序主题活动_浅谈vue3中effect与computed的亲密关系

puted的亲密关系       puted的亲密关系,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在我刚看完vue3响应式的时候,心中就有一个不可磨灭的谜团,让我茶不思饭不想,总想生病。那么这个谜团是什么呢?就是在响应式中一直穿行在tranger跟track之间的effect。如果单纯的响应式原理根本就用不上effect,那么effect到底是干什么的呢?

船到桥头自然直,柳岸花明又一村。苦心人天不负,偶然间我看到了effect测试代码用例!

it('should observe basic properties', () = {
 let dummy
 const counter = reactive({ num: 0 })
 effect(() = (dummy = counter.num))
 expect(dummy).toBe(0)
 counter.num = 7
 expect(dummy).toBe(7)

解释一下,这段代码

首先声明dummy变量,然后在effect的回调中把已响应的对象counter的num属性赋值给dummy 然后做断言判断 dummy是否等于 0 将 counter.num 赋值 7 ,然后 dummy 也变成了 7 !

这,,,让我想到了什么??

const value = reactive { foo : number } ({})
const cValue = computed(() = value.foo)
expect(cValue.value).toBe(undefined)
value.foo = 1
expect(cValue.value).toBe(1)

哈哈哈

阿哈哈哈哈

hhhhhhhhhhhhhhhhhhhh

忍不住想仰天长啸!!

果然跟我猜想的一样!!!我终于直到effect是个什么鬼了,顾名思义effect是副作用的意思,也就是说它是响应式副产品,每次触发了 get 时收集effect,每次set时在触发这些effects。puted。

我就不写一些乱七八糟的判断了,让大家能够看的更加清楚

puted (fn) {
 let value = undefined
 const runner = effect(fn, {
 // 如果lazy不置为true的话,每次创建effect的时候都会立即执行一次
 // puted显然是不需要的
 lazy: true
 // puted的值
 // 如果不用对象的 puted() 
 return {
 get value() {
 return runner()
// 使用起来是这样的
const value = reactive({})
const cValue = computed(() = value.foo)
value.foo = 1
console.log(cValue.value) // 1

这也太简单了吧,那么重点来了,effect怎么实现的呢?

别着急,我们先捋一下逻辑

首先 如果 effect 回调内有已响应的对象被触发了 get 时,effect就应该被储存起来 然后,我们需要一个储存effect的地方,在effect函数调用的时候就应该把effect放进这个储存空间,在vue中使用的是一个数组activeReactiveEffectStack = [] 再后,每个target被触发的时候,都可能有多个effect,所以每个target需要有一个对应的依赖收集器 de凡科抠图,等到 set 时遍历 de凡科抠图 执行 effect() 然而,这个依赖收集器 de凡科抠图 不能放在 target 本身上,这样会使数据看起来不是很简洁,还会存在多余无用的数据,所以我们需要一个 map 集合来储存 target 跟 de凡科抠图 的关系, 在vue中这个储存集合叫 targetMap 。

几个概念

track 追踪器,在 get 时调用该函数,将所有 get 的 target 跟 key 以及 effect 建立起对应关系

// 比如 const react = reactive({a: { b: 2 })
// react.a 时 target - {a: { b: 2 } key - a 
// targetMap 储存了 target -- Map -- key -- Set -- dep -- effect 
// 当调用 react.a.b.c.d.e 时 de凡科抠图Map
// {"a" = Set(1)} -- Set -- effect
// {"b" = Set(1)}
// {"c" = Set(1)}
// {"d" = Set(1)}
// {"e" = Set(1)}
export function track(target: any, key: string) {
 const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1];
 if (effect) {
 let de凡科抠图Map = targetMap.get(target);
 if (de凡科抠图Map === void 0) {
 targetMap.set(target, (de凡科抠图Map = new Map()));
 let dep = de凡科抠图Map.get(key!);
 if (!dep) {
 de凡科抠图Map.set(key!, (dep = new Set()));
 if (!dep.has(effect)) {
 dep.add(effect);
 effect.de凡科抠图.push(dep);

trigger 触发器,这个就比较好理解了,拿到target key下的对应的所有 effect,然后遍历执行 effect()

export function trigger(target: any, key : string | symbol) {
 const de凡科抠图Map: any = targetMap.get(target);
 const effects: any = new Set()
 if (de凡科抠图Map de凡科抠图Map.get(key)) {
 de凡科抠图Map.get(key).forEach((dep: any) = {
 effects.add(dep)
 effects.forEach((e: any) = e())

effect 函数实现

// 暴露的 effect 函数
export function effect(
 fn: Function,
 options: any = EMPTY_OBJ
): any {
 if ((fn as any).isEffect) {
 fn = (fn as any).raw
 const effect = createReactiveEffect(fn, options)
 // 如果不是 lazy,则会立即执行一次
 if (!options.lazy) {
 effect()
 return effect
// 创建 effect
function createReactiveEffect(
 fn: Function,
 options: any
): any {
 const effect = function effect(...args: any): any {
 return run(effect as any, fn, args)
 } as any
 effect.isEffect = true
 effect.active = true
 effect.raw = fn
 effect.scheduler = options.scheduler
 effect.onTrack = options.onTrack
 effect.onTrigger = options.onTrigger
 effect.onStop = options.onStop
 puted = puted
 effect.de凡科抠图 = []
 return effect
// 执行函数,执行完之后会将储存的 effect 删除
// 这是函数 effect 的所有执行,所经历的完整的声明周期
function run(effect: any, fn: Function, args: any[]): any {
 if (!effect.active) {
 return fn(...args)
 if (activeReactiveEffectStack.indexOf(effect) === -1) {
 try {
 activeReactiveEffectStack.push(effect)
 return fn(...args)
 } finally {
 activeReactiveEffectStack.pop()

一口气写了这么多,最后总结一下。在大家看源码的时候,如果发现有哪个地方无从下手的话,可以先从测试用例开始看。因为测试用例可以很清楚的知道这个函数想要达到什么效果,然后从效果上想,为什么这么做,如果我自己写的话应该怎么写,这样一点点就能揣摩出作者的意图了。再根据源码结合自己的想法你就能够学到很多。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持凡科。