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...