陣列怎麼取值 Vue漫談 怎麼個依賴法(二)

2021-10-12 09:41:21 字數 4181 閱讀 3244

computed如果定義了但是沒被引用?為什麼就不會訂閱dep?

如果template只依賴了計算屬性a,a又依賴了state裡的a屬性,很明顯元件沒有直接對於a的依賴,但是為什麼修改了a元件也能更新呢?

和watch一樣,計算屬性的初始化也在src/core/instance/state.js中可以找到:

const computedwatcheroptions = 

function initcomputed (vm: component, computed: object) ".`,vm)

getter = noop}}

// create internal watcher for the computed property.

watchers[key] = new watcher(vm, getter, noop, computedwatcheroptions)

// component-defined computed properties are already defined on the

// component prototype. we only need to define computed properties defined

// at instantiation here.

if (!(key in vm)) else if (process.env.node_env !== 'production') " is already defined in data.`, vm)

} else if (vm.$options.props && key in vm.$options.props) " is already defined as a prop.`, vm)}}

}}

可以看到每個key對應的計算屬性都新建了乙個watcher例項,並且最頂部有乙個watcher option引數,lazy = true表示了這是個延遲計算的watcher。而在watcher建構函式裡(具體**看上篇),lazy watcher有以下2句特殊的邏輯:

this.dirty = this.lazy // for lazy watchers

this.value = this.lazy ? undefined : this.get()

第一句直接對lazy watcher下了定義:你要是lazy,那你就是髒的,別人要用你,必須重新取值,也就是要呼叫watcher.evalute()方法,這樣才能保證你是最新的:

/**

* evaluate the value of the watcher.

* this only gets called for lazy watchers.

*/evaluate ()

第二句也就解釋了為什麼計算屬性如果沒被引用,就不會收集依賴。因為對於lazy watcher,第一次根本不會呼叫this.get()方法

還剩下最後乙個問題,試想一下這樣的元件:

vue.component('rectangle', ;

},computed:

},template: `

area: }

`});

width和height沒有直接被template引用,那為什麼修改他們元件也會更新?之前我們說過,元件的watcher是普通watcher,他會繫結元件的render方法,在第一次呼叫render的時候收集所以引用的依賴,那這種間接的依賴是怎麼收集的呢?

我在沒看計算屬性**前,第一直覺就是為每個計算屬性也new乙個dep例項,然後攔截對應的計算屬性函式,通知計算屬性dep對應的watcher。這樣想想好像說得通,但是vue不是這樣實現的。

先說結論,對於上面這種情況,vue會直接收集到computed的2個dep,然後訂閱他們,下面我們就來看看vue的奇技淫巧。

首先,我們得回頭看一下dep的pushtarget和poptarget方法:

// the current target watcher being evaluated.

// this is globally unique because there could be only one

// watcher being evaluated at any time.

dep.target = null

const targetstack =

export function pushtarget (_target: watcher)

export function poptarget ()

這裡有乙個targetstack陣列,或者說是棧,這個棧的作用就是當watcher.get執行過程中,如果遇到了別的watcher,就先把當前的watcher入棧,先執行別的watcher。上面說的間接依賴收集就借助了這個方法。

上面講initcomputed的時候,最後還有乙個方法沒講,就是definecomputed,它的主要作用就是把computed屬性**到元件例項上,並且,攔截了計算屬性的getter,setter:

export function definecomputed (target: any, key: string, userdef: object | function)  else 

object.defineproperty(target, key, sharedpropertydefinition)

}

我們就主要關注第乙個if,它的get被設定為了乙個createcomputedgetter的返回值,set則被設定成了noop,也就是空方法(所以如果直接修改乙個computed是沒啥用的)。繼續看看這個方法:

function createcomputedgetter (key) 

if (dep.target)

return watcher.value}}}

先看第乙個if(watcher.dirty),表明這是乙個沒有及時更新的lazy watcher,或者簡單的說就是計算屬性的watcher, 就會呼叫evalute()來取值,在這個取值過程中,依賴就會被收集了。所以只要引用,計算屬性就能訂閱依賴的變化。

重點來了,第二個if(dep.target)裡,呼叫了watcher的depend。我最初看的時候,無數次忽略了這個watcher.depend,總是習慣性的以為是dep.depend,然後想了一遍又一遍,一直沒想到**可以收集到間接依賴。直到我反覆的打斷點,才突然發現,這tm是watcher.depend,不是dep.depend,那他是怎麼實現的呢?

/**

* depend on all deps collected by this watcher.

*/depend ()

}

這個裡面,this指向的是當前計算屬性的watcher,而他的deps就是在dep.depend裡被新增到watcher裡的。

這裡可能不太好理解,我們來梳理一下。

首先,1個dep可以被多個watcher訂閱,但是1個watcher也可以訂閱多個依賴,比如我的面積屬性,就依賴了長和寬。所以這樣一來,dep和watcher其實是多對多的關係。

那知道乙個watcher所依賴的dep有什麼用呢?沒錯,就是依賴轉移(我自己取得名字<_>

no no no!

再來看一下dep.depend實現:

depend ()
}回想一下,上上一步,我們呼叫了watcher.evalute(),它的裡面又呼叫了watcher.get(),get的最後又呼叫了poptarget,而poptarget返回的是出棧後棧頂的元素。而這個元素就是元件的watcher,所以一切迎刃而解了。通過遍歷計算屬性watcher的deps,讓元件watcher去訂閱他們。妙,真的是妙啊!

弄懂了這個問題,我甚至興奮得去樓下超市買了一袋幹拌麵!

不知道尤大寫得時候花了多久想到這種方式,反正我光理解就花了很久,差距!

c 中空陣列 c 中怎麼清空乙個陣列

class program static void main string args int num 4 要刪除元素的下標 int arr new int console.writeline 刪除前該陣列的長度 arr.length foreach int a in arr console.writ...

怎麼判斷乙個object是否是陣列 array ?

方法一 instanceof instanceof 用於判斷乙個變數是否某個物件的例項 var arr console.log arr instanceof array 返回true 方法二 constructor constructor 屬性返回對建立此物件的陣列函式的引用,就是返回物件相對應的建...

關於 js 2個陣列取差集怎麼取

例如求var arr1 1 var arr2 1,2 的差集 方法一 1 array.prototype.diff function a 3 4 1,2 diff 1 2 方法二 1 var isnan number.isnan 2 var difference function arr1,arr2...