這一節(jié),我們的示例代碼如下

默認(rèn)情況下頁(yè)面將渲染出"default",當(dāng)我們第一次點(diǎn)擊onChangeIndex函數(shù)后將顯示"三歲就會(huì)寫bug",同時(shí)打印出''update'',當(dāng)再次點(diǎn)擊則頁(yè)面不會(huì)有變化,但是仍然打印出"update";當(dāng)點(diǎn)擊onChangeName后頁(yè)面展示"三歲就會(huì)寫bug哦",同時(shí)打印"update",當(dāng)再次點(diǎn)擊時(shí),則頁(yè)面無(wú)變化同時(shí)不會(huì)打印"update".
那么為什么會(huì)這樣呢?
幾個(gè)小問(wèn)題
我們之前在分析組件的createComponent和組件的init時(shí)候都跳過(guò)了部分關(guān)于computed的邏輯
????在組件創(chuàng)建的過(guò)程中,調(diào)用extend,判斷computed是否存在,然后進(jìn)行計(jì)算屬性的init,繼而調(diào)用defineComputed

? ??在組件的init過(guò)程中,調(diào)用initState方法,該方法除了對(duì)props、data執(zhí)行了相關(guān)邏輯外,還判斷了computed是否存在,并在存在時(shí)調(diào)用其init,繼而調(diào)用defineComputed

? ? ? ? 也就是說(shuō),vue在構(gòu)建組件構(gòu)造器時(shí)候便已經(jīng)對(duì)compoted進(jìn)行了處理,而處理函數(shù)即defineComputed,那么我現(xiàn)在有3個(gè)疑問(wèn):a-為什么要提前處理?b-兩處處理有什么不同?c-只處理一次行不行?
? ??????首先看extend中的處理

? ? ? ? ? ? ? ? ? ? ?首先拿到組件的options對(duì)象,并對(duì)computed進(jìn)行遍歷,并對(duì)每一個(gè)key(name函數(shù))調(diào)用defineComputed,入?yún)榻M件的原型、name函數(shù)
? ??????????再看init過(guò)程

? ? ? ? ? ? 可以看到,兩次都是拿到computed的key遍歷調(diào)用defineComputed,不一樣的是,一次傳入的target是實(shí)例的原型,一次則是vm實(shí)例。我們都知道this會(huì)首先查找實(shí)例本身,若不存在則跟隨原型鏈查找原型對(duì)象。且for...in循環(huán)具有查找原型鏈的能力。也就是說(shuō),同一個(gè)組件,只會(huì)在構(gòu)建構(gòu)造器時(shí)候執(zhí)行一次。當(dāng)對(duì)組件進(jìn)行初始化時(shí)候,for...in將查找到原型上存在的key,故不會(huì)重復(fù)多次調(diào)用defineComputed。這可以認(rèn)為是一種"數(shù)據(jù)共享",是一種優(yōu)化手段
? ? ? ? ? ? 所以,之前的疑問(wèn)可以這么回答:為了避免當(dāng)在同一個(gè)頁(yè)面中多次使用同一個(gè)組件時(shí)每次都在組件中定義一遍key造成性能浪費(fèi),便利用原型鏈特性一勞永逸的在構(gòu)建組件階段放置到原型鏈上以避免走重復(fù)邏輯(疑問(wèn)a、b);只在組件上定義會(huì)存在a、b提出的性能問(wèn)題,而只在原型上定義則無(wú)法針對(duì)如mixin一類后植入的key進(jìn)行處理(疑問(wèn)c)
創(chuàng)建過(guò)程
當(dāng)執(zhí)行組件初始化過(guò)程中會(huì)調(diào)用initState并判斷computed存在執(zhí)行initComputed,傳入組件實(shí)例和在組件中定義的computed對(duì)象(我們這里即name函數(shù))

? ? ? ? --向組件實(shí)例掛載_computedWatchers,默認(rèn)是空對(duì)象,并通過(guò)Object.create賦予其原型鏈訪問(wèn)權(quán)力
? ? ? ? --isSSR在瀏覽器環(huán)境下為false,進(jìn)入判斷,進(jìn)行watcher實(shí)例化。入?yún)椋航M件實(shí)例、getter函數(shù)(name函數(shù))、noop空函數(shù)、{?lazy:?true?}。實(shí)例化watcher的關(guān)鍵信息如下



? ? ? ? --使用for...in循環(huán)拿到每一個(gè)key,即我們定義的每一個(gè)函數(shù)
? ? ? ? --向?qū)嵗腳computedWatchers上綁定一個(gè)watcher,從之前文章的分析我們知道watcher將在一定的時(shí)機(jī)觸發(fā)update進(jìn)行patch最終渲染為dom
? ? ? ? --調(diào)用defineComputed,將組件實(shí)例、每一個(gè)計(jì)算屬性的key及其對(duì)應(yīng)的處理函數(shù)(name函數(shù))傳入

? ? ? ? ? ? ? ? --shouldCache為true????
? ? ? ? ? ? ? ? --sharedPropertyDefinition.get對(duì)應(yīng)的是一個(gè)匿名函數(shù)

? ? ? ? ? ? ? ? --由于將sharedPropertyDefinition作為Object.defineProperty的屬性描述符,故當(dāng)訪問(wèn)this.name時(shí),將會(huì)觸發(fā)攔截從而調(diào)用sharedPropertyDefinition的get方法,而get方法指向上一步返回的computedGetter函數(shù)
計(jì)算過(guò)程--default
? ? ? ? 當(dāng)vue將組件編譯為render函數(shù)的過(guò)程中將對(duì)模板中的變量name進(jìn)行訪問(wèn),此時(shí)將觸發(fā)sharedPropertyDefinition的get,即computedGetter函數(shù)
? ? ? ? --拿到我們?cè)趧?chuàng)建過(guò)程中保存的每一個(gè)computed.key對(duì)應(yīng)的watcher
? ? ? ? --調(diào)用evalute函數(shù)

? ??????????????調(diào)用get函數(shù),求值

? ? ? ? ? ? ? ? ? ? --向dep.target保存this
? ? ? ? ? ? ? ? ? ? --調(diào)用getter函數(shù),即name函數(shù)

? ? ? ? --調(diào)用depend函數(shù)

? ? ? ? ? ? ? ? 這實(shí)際上調(diào)用的是

因此,得出的值為"default"
計(jì)算過(guò)程--三歲就會(huì)寫bug
? ? ? ? 當(dāng)我們點(diǎn)擊onChangeIndex函數(shù)將手動(dòng)把saveIndex加加,由于saveIndex是在data中定義的響應(yīng)式數(shù)據(jù),故它將首先走向get方法進(jìn)行依賴收集,此時(shí)dep中將有兩個(gè)訂閱者:計(jì)算屬性name和saveIndex

? ? ? ? 加加的操作則觸發(fā)saveIndex的set方法,將觸發(fā)dep的notify

? ? ? ? ? ? 繼而調(diào)用watcher的update,由于第一個(gè)dep是計(jì)算屬性的,故只會(huì)執(zhí)行到this.lazy中將this.dirty置為true后便會(huì)調(diào)用saveIndex對(duì)應(yīng)watcher的update,此次將觸發(fā)queueWatcher執(zhí)行render,而在render的過(guò)程中將再次訪問(wèn)name,此時(shí)this.saveIndex>0成立,獲取firstName和lastName并計(jì)算返回

因此,得出的值為"三歲就會(huì)寫bug"
最后關(guān)于"當(dāng)點(diǎn)擊onChangeName后頁(yè)面展示"三歲就會(huì)寫bug哦",同時(shí)打印"update",當(dāng)再次點(diǎn)擊時(shí),則頁(yè)面無(wú)變化同時(shí)不會(huì)打印"update".",主要是因?yàn)樵趕et攔截中進(jìn)行了值比較,當(dāng)相等時(shí)則return,因此不會(huì)重新render,故而無(wú)法調(diào)用update

可以看到,當(dāng)其依賴項(xiàng)發(fā)生變化時(shí)總是會(huì)重新求值,但是如果求出的值相等時(shí)vue并沒(méi)有做限制,而是每次都重新計(jì)算了一次。這樣的不合理可能是由于我當(dāng)前看的源碼版本(2.5.2)較老導(dǎo)致的,畢竟尤大這樣的神級(jí)人物不會(huì)考慮不到這一點(diǎn)