vue響應(yīng)式詳解(重學(xué)前端-vue篇1)

數(shù)據(jù)發(fā)生變化后,會(huì)重新對(duì)頁(yè)面渲染瘟栖,這就是Vue響應(yīng)式

響應(yīng)式圖例

2 想完成這個(gè)過(guò)程横蜒,我們需要做些什么

偵測(cè)數(shù)據(jù)的變化

收集視圖依賴了哪些數(shù)據(jù)

數(shù)據(jù)變化時(shí),自動(dòng)“通知”需要更新的視圖部分右核,并進(jìn)行更新

它們對(duì)應(yīng)專業(yè)俗語(yǔ)分別是:

數(shù)據(jù)劫持 / 數(shù)據(jù)代理

依賴收集

發(fā)布訂閱模式

3 如何偵測(cè)數(shù)據(jù)的變化

有兩種辦法可以偵測(cè)到變化:

使用Object.defineProperty和ES6的Proxy慧脱,這就是進(jìn)行數(shù)據(jù)劫持或數(shù)據(jù)代理。

3.1 Object.defineProperty實(shí)現(xiàn)

Vue通過(guò)設(shè)定對(duì)象屬性的?setter/getter?方法來(lái)監(jiān)聽(tīng)數(shù)據(jù)的變化贺喝,通過(guò)getter進(jìn)行依賴收集菱鸥,而每個(gè)setter方法就是一個(gè)觀察者,在數(shù)據(jù)變更的時(shí)候通知訂閱者更新視圖躏鱼。

代碼如下:

functionrender()?{

//set的時(shí)候會(huì)走這里氮采,重新渲染

console.log('模擬視圖渲染')

}

letdata?=?{

name:'浪里行舟',

location:?{?x:?100,?y:?100?}

}

observe(data)

定義核心函數(shù)

functionobserve?(obj)?{?//?我們來(lái)用它使對(duì)象變成可觀察的

//?判斷類型

if(!obj?||?typeof?obj?!=='object')?{

return

}

Object.keys(obj).forEach(key?=>?{

defineReactive(obj,?key,?obj[key])

})

functiondefineReactive?(obj,?key,?value)?{

//?遞歸子屬性

observe(value)

Object.defineProperty(obj,?key,?{

enumerable:true,?//可枚舉(可以遍歷)

configurable:true,?//可配置(比如可以刪除)

get:functionreactiveGetter()?{

console.log('get',?value)?//?監(jiān)聽(tīng)

returnvalue

},

set:functionreactiveSetter?(newVal)?{

observe(newVal)?//如果賦值是一個(gè)對(duì)象,也要遞歸子屬性

if(newVal?!==?value)?{

console.log('set',?newVal)?//?監(jiān)聽(tīng)

render()

value?=?newVal

}

}

})

}

}

改變data的屬性挠他,會(huì)出發(fā)set扳抽;然后獲取data的屬性,會(huì)觸發(fā)get

data.location?=?{

x:?1000,

y:?1000

}?//打印set{x:?1000,y:?1000}?模擬視圖渲染

data.name?//打印???get?浪里行舟

上面這段代碼的主要作用在于:

observe這個(gè)函數(shù)傳入一個(gè)?obj(需要被追蹤變化的對(duì)象)殖侵,通過(guò)遍歷所有屬性的方式對(duì)該對(duì)象的每一個(gè)屬性都通過(guò)?defineReactive?處理,給每個(gè)屬性加上set和get方法,以此來(lái)達(dá)到實(shí)現(xiàn)偵測(cè)對(duì)象變化贸呢。值得注意的是,observe 會(huì)進(jìn)行遞歸調(diào)用拢军。

那我們?nèi)绾蝹蓽y(cè)Vue中data 中的數(shù)據(jù)楞陷,其實(shí)也很簡(jiǎn)單:

class?Vue?{

/*?Vue構(gòu)造類?*/

constructor(options)?{

this._data?=?options.data;

observer(this._data);

}

}

這樣我們只要 new 一個(gè) Vue 對(duì)象,就會(huì)將 data 中的數(shù)據(jù)進(jìn)行追蹤變化茉唉。

但是我們發(fā)現(xiàn)一個(gè)問(wèn)題固蛾,上面的代碼無(wú)法檢測(cè)到對(duì)象屬性的添加或刪除(如data.location.a=1,增加一個(gè)a屬性)结执。

這是因?yàn)?Vue 通過(guò)Object.defineProperty來(lái)將對(duì)象的key轉(zhuǎn)換成getter/setter的形式來(lái)追蹤變化,但getter/setter只能追蹤一個(gè)數(shù)據(jù)是否被修改艾凯,無(wú)法追蹤新增屬性和刪除屬性献幔。如果是刪除屬性,我們可以用vm.$delete實(shí)現(xiàn)趾诗,那如果是新增屬性蜡感,該怎么辦呢?

可以使用 Vue.set(location, a, 1) 方法向嵌套對(duì)象添加響應(yīng)式屬性;

也可以給這個(gè)對(duì)象重新賦值恃泪,比如data.location = {...data.location,a:1}

Object.defineProperty 不能監(jiān)聽(tīng)數(shù)組的變化郑兴,需要進(jìn)行數(shù)組方法的重寫(xiě)

3.2 Proxy實(shí)現(xiàn)

Proxy 是?JavaScript 2015?的一個(gè)新特性。Proxy 的代理是針對(duì)整個(gè)對(duì)象的贝乎,而不是對(duì)象的某個(gè)屬性情连,因此不同于 Object.defineProperty 的必須遍歷對(duì)象每個(gè)屬性,Proxy 只需要做一層代理就可以監(jiān)聽(tīng)同級(jí)結(jié)構(gòu)下的所有屬性變化览效,當(dāng)然對(duì)于深層結(jié)構(gòu)却舀,遞歸還是需要進(jìn)行的。此外Proxy支持代理數(shù)組的變化锤灿。

functionrender()?{

console.log('模擬視圖的更新')

}

letobj?=?{

name:'前端工匠',

age:?{?age:?100?},

arr:?[1,?2,?3]

}

lethandler?=?{

get(target,?key)?{

//?如果取的值是對(duì)象就再對(duì)這個(gè)對(duì)象進(jìn)行數(shù)據(jù)劫持

if(typeof?target[key]?=='object'&&?target[key]?!==?null)?{

returnnew?Proxy(target[key],?handler)

}

returnReflect.get(target,?key)

},

set(target,?key,?value)?{

//key為length時(shí)禁筏,表示遍歷完了最后一個(gè)屬性

if(key?==='length')returntrue

render()

returnReflect.set(target,?key,?value)

}

}

letproxy?=?new?Proxy(obj,?handler)

proxy.age.name?='浪里行舟'//?支持新增屬性

console.log(proxy.age.name)?//?模擬視圖的更新?浪里行舟

proxy.arr[0]?='浪里行舟'//支持?jǐn)?shù)組的內(nèi)容發(fā)生變化

console.log(proxy.arr)?//?模擬視圖的更新?['浪里行舟',?2,?3?]

proxy.arr.length--?//?無(wú)效

以上代碼不僅精簡(jiǎn),而且還是實(shí)現(xiàn)一套代碼對(duì)對(duì)象和數(shù)組的偵測(cè)都適用衡招。不過(guò)Proxy兼容性不太好篱昔!

4. 收集依賴

4.1 為什么要收集依賴

我們之所以要觀察數(shù)據(jù),其目的在于當(dāng)數(shù)據(jù)的屬性發(fā)生變化時(shí)始腾,可以通知那些曾經(jīng)使用了該數(shù)據(jù)的地方州刽。比如例子中,模板中使用了location 數(shù)據(jù)浪箭,當(dāng)它發(fā)生變化時(shí)穗椅,要向使用了它的地方發(fā)送通知。

letglobalData?=?{

text:'浪里行舟'

};

lettest1?=?new?Vue({

template:

`

{{text}}

`,

data:?globalData

});

lettest2?=?new?Vue({

template:

`

{{text}}

`,

data:?globalData

});

如果我們執(zhí)行下面這條語(yǔ)句:

globalData.text?='前端工匠';

此時(shí)我們需要通知?test1?以及?test2?這兩個(gè)Vue實(shí)例進(jìn)行視圖的更新,我們只有通過(guò)收集依賴才能知道哪些地方依賴我的數(shù)據(jù)奶栖,以及數(shù)據(jù)更新時(shí)派發(fā)更新匹表。那依賴收集是如何實(shí)現(xiàn)的?其中的核心思想就是“事件發(fā)布訂閱模式”宣鄙。接下來(lái)我們先介紹兩個(gè)重要角色--?訂閱者 Dep和觀察者 Watcher?袍镀,然后闡述收集依賴的如何實(shí)現(xiàn)的。

4.2 訂閱者 Dep

為什么引入 Dep:

收集依賴需要為依賴找一個(gè)存儲(chǔ)依賴的地方冻晤,為此我們創(chuàng)建了Dep,它用來(lái)收集依賴苇羡、刪除依賴和向依賴發(fā)送消息等。

于是我們先來(lái)實(shí)現(xiàn)一個(gè)訂閱者 Dep 類鼻弧,用于解耦屬性的依賴收集和派發(fā)更新操作设江,說(shuō)得具體點(diǎn):它的主要作用是用來(lái)存放 Watcher 觀察者對(duì)象锦茁。我們可以把Watcher理解成一個(gè)中介的角色,數(shù)據(jù)發(fā)生變化時(shí)通知它叉存,然后它再通知其他地方码俩。

Dep的簡(jiǎn)單實(shí)現(xiàn):

class?Dep?{

constructor()?{

/*?用來(lái)存放Watcher對(duì)象的數(shù)組?*/

this.subs?=?[];

}

/*?在subs中添加一個(gè)Watcher對(duì)象?*/

addSub?(sub)?{

this.subs.push(sub);

}

/*?通知所有Watcher對(duì)象更新視圖?*/

notify()?{

this.subs.forEach((sub)?=>?{

sub.update();

})

}

}

以上代碼主要做兩件事情:

用 addSub 方法可以在目前的?Dep?對(duì)象中增加一個(gè)?Watcher?的訂閱操作;

用 notify 方法通知目前?Dep?對(duì)象的?subs?中的所有?Watcher?對(duì)象觸發(fā)更新操作歼捏。 所以當(dāng)需要依賴收集的時(shí)候調(diào)用 addSub握玛,當(dāng)需要派發(fā)更新的時(shí)候調(diào)用 notify。

調(diào)用也很簡(jiǎn)單:

letdp?=?new?Dep()

dp.addSub(()?=>?{//依賴收集的時(shí)候

console.log('emit?here')

})

dp.notify()//派發(fā)更新的時(shí)候

5 觀察者 Watcher

5.1 為什么引入Watcher

Vue 中定義一個(gè) Watcher 類來(lái)表示觀察訂閱依賴甫菠。至于為啥引入Watcher,《深入淺出vue.js》給出了很好的解釋:

當(dāng)屬性發(fā)生變化后冕屯,我們要通知用到數(shù)據(jù)的地方寂诱,而使用這個(gè)數(shù)據(jù)的地方有很多,而且類型還不一樣安聘,既有可能是模板痰洒,也有可能是用戶寫(xiě)的一個(gè)watch,這時(shí)需要抽象出一個(gè)能集中處理這些情況的類。然后浴韭,我們?cè)谝蕾囀占A段只收集這個(gè)封裝好的類的實(shí)例進(jìn)來(lái)丘喻,通知也只通知它一個(gè),再由它負(fù)責(zé)通知其他地方念颈。

依賴收集的目的是:?將觀察者 Watcher 對(duì)象存放到當(dāng)前閉包中的訂閱者 Dep 的 subs 中泉粉。形成如下所示的這樣一個(gè)關(guān)系(圖參考《剖析 Vue.js 內(nèi)部運(yùn)行機(jī)制》)。

5.2 Watcher的簡(jiǎn)單實(shí)現(xiàn)

class?Watcher?{

constructor(obj,?key,?cb)?{

//?將?Dep.target?指向自己

//?然后觸發(fā)屬性的?getter?添加監(jiān)聽(tīng)

//?最后將?Dep.target?置空

Dep.target?=?this

this.cb?=?cb

this.obj?=?obj

this.key?=?key

this.value?=?obj[key]

Dep.target?=?null

}

update()?{

//?獲得新值

this.value?=?this.obj[this.key]

//?我們定義一個(gè)?cb?函數(shù)榴芳,這個(gè)函數(shù)用來(lái)模擬視圖更新嗡靡,調(diào)用它即代表更新視圖

this.cb(this.value)

}

}

以上就是 Watcher 的簡(jiǎn)單實(shí)現(xiàn),在執(zhí)行構(gòu)造函數(shù)的時(shí)候?qū)?Dep.target 指向自身窟感,從而使得收集到了對(duì)應(yīng)的 Watcher讨彼,在派發(fā)更新的時(shí)候取出對(duì)應(yīng)的 Watcher ,然后執(zhí)行 update 函數(shù)。

依賴的本質(zhì):

所謂的依賴柿祈,其實(shí)就是Watcher哈误。

至于如何收集依賴,總結(jié)起來(lái)就一句話:

在getter中收集依賴躏嚎,在setter中觸發(fā)依賴蜜自。先收集依賴,即把用到該數(shù)據(jù)的地方收集起來(lái)卢佣,然后等屬性發(fā)生變化時(shí)袁辈,把之前收集好的依賴循環(huán)觸發(fā)一遍就行了。

具體來(lái)說(shuō)珠漂,當(dāng)外界通過(guò)Watcher讀取數(shù)據(jù)時(shí)晚缩,便會(huì)觸發(fā)getter從而將Watcher添加到依賴中尾膊,哪個(gè)Watcher觸發(fā)了getter,就把哪個(gè)Watcher收集到Dep中。當(dāng)數(shù)據(jù)發(fā)生變化時(shí)既荚,會(huì)循環(huán)依賴列表阀参,把所有的Watcher都通知一遍。

最后我們對(duì) defineReactive 函數(shù)進(jìn)行改造抓谴,在自定義函數(shù)中添加依賴收集和派發(fā)更新相關(guān)的代碼,實(shí)現(xiàn)了一個(gè)簡(jiǎn)易的數(shù)據(jù)響應(yīng)式:

functionobserve?(obj)?{

//?判斷類型

if(!obj?||?typeof?obj?!=='object')?{

return

}

Object.keys(obj).forEach(key?=>?{

defineReactive(obj,?key,?obj[key])

})

functiondefineReactive?(obj,?key,?value)?{

observe(value)??//?遞歸子屬性

letdp?=?new?Dep()?//新增

Object.defineProperty(obj,?key,?{

enumerable:true,?//可枚舉(可以遍歷)

configurable:true,?//可配置(比如可以刪除)

get:functionreactiveGetter()?{

console.log('get',?value)?//?監(jiān)聽(tīng)

//?將?Watcher?添加到訂閱

if(Dep.target)?{

dp.addSub(Dep.target)?//?新增

}

returnvalue

},

set:functionreactiveSetter?(newVal)?{

observe(newVal)?//如果賦值是一個(gè)對(duì)象,也要遞歸子屬性

if(newVal?!==?value)?{

console.log('set',?newVal)?//?監(jiān)聽(tīng)

render()

value?=?newVal

//?執(zhí)行?watcher?的?update?方法

dp.notify()?//新增

}

}

})

}

}

class?Vue?{

constructor(options)?{

this._data?=?options.data;

observer(this._data);

/*?新建一個(gè)Watcher觀察者對(duì)象寞缝,這時(shí)候Dep.target會(huì)指向這個(gè)Watcher對(duì)象?*/

new?Watcher();

console.log('模擬視圖渲染');

}

}

當(dāng) render function 被渲染的時(shí)候,讀取所需對(duì)象的值癌压,會(huì)觸發(fā)?reactiveGetter?函數(shù)把當(dāng)前的 Watcher 對(duì)象(存放在 Dep.target 中)收集到 Dep 類中去。之后如果修改對(duì)象的值荆陆,則會(huì)觸發(fā)?reactiveSetter?方法滩届,通知 Dep 類調(diào)用?notify?來(lái)觸發(fā)所有 Watcher 對(duì)象的?update?方法更新對(duì)應(yīng)視圖。

完整流程圖:

在 new Vue() 后被啼, Vue 會(huì)調(diào)用_init 函數(shù)進(jìn)行初始化帜消,也就是init 過(guò)程,在 這個(gè)過(guò)程Data通過(guò)Observer轉(zhuǎn)換成了getter/setter的形式浓体,來(lái)對(duì)數(shù)據(jù)追蹤變化泡挺,當(dāng)被設(shè)置的對(duì)象被讀取的時(shí)候會(huì)執(zhí)行g(shù)etter 函數(shù),而在當(dāng)被賦值的時(shí)候會(huì)執(zhí)行 setter函數(shù)命浴。

當(dāng)外界通過(guò)Watcher讀取數(shù)據(jù)時(shí)娄猫,會(huì)觸發(fā)getter從而將Watcher添加到依賴中。

在修改對(duì)象的值的時(shí)候生闲,會(huì)觸發(fā)對(duì)應(yīng)的setter稚新, setter通知之前依賴收集得到的 Dep 中的每一個(gè) Watcher,告訴它們自己的值改變了跪腹,需要重新渲染視圖褂删。這時(shí)候這些 Watcher就會(huì)開(kāi)始調(diào)用 update 來(lái)更新視圖。

最后完整的響應(yīng)式代碼:

大概結(jié)構(gòu)

//defineReactive是對(duì)Observer的抽離

const?defineReactive?=function(obj,?key)?{

//?以下代碼省略

}

const?Vue?=function(options)?{

console.log("Vue",this)

//打印1??Vue?{

_data:{

text:"123"

get?text:???get()

settext:??set(newVal)

},

mount:???(),

render:???()

}

//?以下代碼省略

}

const?Watcher?=function(vm,?fn)?{

console.log("Watcher",this)

//打印3?Watcher??this是下面的Dep中subs的對(duì)象

//?以下代碼省略

}

const?Dep?=function()?{

console.log("Dep",this)

//打印2??Dep???{

target:?null,

subs:?[

{????????//是一個(gè)Watcher實(shí)例

subs:?Array(1)

0:?Watcher

vm:?{????//是一個(gè)Vue實(shí)例

_data:{

text:"123",//該屬性有了get和set方法

get?text:???get(),

settext:??set(newVal)

},

mount:???(),

render:???()

},

addDep:???(dep),

update:???(),

value:?undefined

}

],

depend:???(),

addSub:???(watcher),

notify:???()

}

//?以下代碼省略

}

const?vue?=?new?Vue({

data()?{

return{

text:'hello?world'

};

}

})

vue.mount();

vue._data.text?='123';

詳細(xì)代碼

const?Observer?=function(data)?{

console.log(1)???//開(kāi)始4?new?Vue的時(shí)候就會(huì)執(zhí)行

//?循環(huán)修改為每個(gè)屬性添加getset

for(letkeyindata)?{

defineReactive(data,?key);

}

}

const?defineReactive?=function(obj,?key)?{

console.log(2)????//開(kāi)始5?new?Vue的時(shí)候就會(huì)執(zhí)行

//?局部變量dep冲茸,用于getset內(nèi)部調(diào)用

const?dep?=?new?Dep();

//?獲取當(dāng)前值

letval?=?obj[key];

Object.defineProperty(obj,?key,?{

//?設(shè)置當(dāng)前描述屬性為可被循環(huán)

enumerable:true,

//?設(shè)置當(dāng)前描述屬性可被修改

configurable:true,

get()?{

console.log(3)//開(kāi)始10??開(kāi)始19

console.log('in?get');

//?調(diào)用依賴收集器中的addSub屯阀,用于收集當(dāng)前屬性與Watcher中的依賴關(guān)系

dep.depend();

returnval;

},

set(newVal)?{

console.log(4)//開(kāi)始15

if(newVal?===?val)?{

return;

}

val?=?newVal;

//?當(dāng)值發(fā)生變更時(shí),通知依賴收集器轴术,更新每個(gè)需要更新的Watcher难衰,

//?這里每個(gè)需要更新通過(guò)什么斷定?dep.subs

dep.notify();

}

});

}

const?observe?=function(data)?{

console.log(5)??//開(kāi)始3?new?Vue的時(shí)候就會(huì)執(zhí)行

returnnew?Observer(data);

}

const?Vue?=function(options)?{

console.log(6)//開(kāi)始1?new?Vue的時(shí)候就會(huì)執(zhí)行

const?self?=?this;

//?將data賦值給this._data逗栽,源碼這部分用的Proxy所以我們用最簡(jiǎn)單的方式臨時(shí)實(shí)現(xiàn)

if(options?&&?typeof?options.data?==='function')?{

console.log(7)//開(kāi)始2???new?Vue的時(shí)候就會(huì)執(zhí)行

this._data?=?options.data.apply(this);

}

//?掛載函數(shù)

this.mount?=function()?{

console.log(8)??//開(kāi)始7??new?Vue以后盖袭,執(zhí)行vue.mount()

new?Watcher(self,?self.render);

}

//?渲染函數(shù)

this.render?=function()?{

console.log(9)?//開(kāi)始9?開(kāi)始18??render函數(shù)執(zhí)行后走到這里

with(self)?{

_data.text;??//這里取data值的時(shí)候,就會(huì)走get方法

}

}

//?監(jiān)聽(tīng)this._data

observe(this._data);??//new?Vue的時(shí)候就會(huì)執(zhí)行,這里執(zhí)行完,就表示new?Vue的過(guò)程執(zhí)行完了

}

const?Watcher?=function(vm,?fn)?{

console.log(10)??//開(kāi)始8??執(zhí)行vue.mount()以后會(huì)走到這里

const?self?=?this;

this.vm?=?vm;

//?將當(dāng)前Dep.target指向自己

Dep.target?=?this;

//?向Dep方法添加當(dāng)前Wathcer

this.addDep?=function(dep)?{

console.log(11)?//開(kāi)始13

dep.addSub(self);

}

//?更新方法鳄虱,用于觸發(fā)vm._render

this.update?=function()?{

console.log(12)//開(kāi)始17

console.log('in?watcher?update');

fn();

}

//?這里會(huì)首次調(diào)用vm._render弟塞,從而觸發(fā)text的get

//?從而將當(dāng)前的Wathcer與Dep關(guān)聯(lián)起來(lái)

this.value?=?fn();???//開(kāi)始9??fn是render函數(shù),這里fn()就會(huì)賦值的時(shí)候執(zhí)行

//?這里清空了Dep.target拙已,為了防止notify觸發(fā)時(shí)决记,不停的綁定Watcher與Dep,

//?造成代碼死循環(huán)

Dep.target?=?null;

}

const?Dep?=function()?{

console.log(13)??//開(kāi)始6??new?Vue的時(shí)候就會(huì)執(zhí)行到new?Dep倍踪,然后執(zhí)行到這里

const?self?=?this;

//?收集目標(biāo)

this.target?=?null;

//?存儲(chǔ)收集器中需要通知的Watcher

this.subs?=?[];

//?當(dāng)有目標(biāo)時(shí)系宫,綁定Dep與Wathcer的關(guān)系

this.depend?=function()?{

console.log(14)??//開(kāi)始11???開(kāi)始20?走了get獲取屬性后,就要進(jìn)行依賴收集

if(Dep.target)?{

console.log(15)//開(kāi)始12

//?這里其實(shí)可以直接寫(xiě)self.addSub(Dep.target)建车,

//?沒(méi)有這么寫(xiě)因?yàn)橄脒€原源碼的過(guò)程扩借。

Dep.target.addDep(self);

}

}

//?為當(dāng)前收集器添加Watcher

this.addSub?=function(watcher)?{

console.log(16)//開(kāi)始14

self.subs.push(watcher);

}

//?通知收集器中所的所有Wathcer,調(diào)用其update方法

this.notify?=function()?{

console.log(17)?//開(kāi)始16

for(leti?=?0;?i?<?self.subs.length;?i?+=?1)?{

self.subs[i].update();

}

}

}

const?vue?=?new?Vue({

data()?{

return{

text:'hello?world'

};

}

})

vue.mount();?//inget

vue._data.text?='123';?//inwatcher?update?/ninget

解析:

一開(kāi)始new Vue 缤至,會(huì)走到46行執(zhí)行Vue構(gòu)造函數(shù)潮罪,打印6

然后46行Vue的入?yún)ptions實(shí)際上是127行的入?yún)data(){}},是一個(gè)包含了data函數(shù)的對(duì)象凄杯,所以options.data是一個(gè)data函數(shù),打印7秉宿。將vue中的data函數(shù)返回的數(shù)據(jù)賦值給_data戒突。

然后走到67行的observe,會(huì)繼續(xù)往上走到41行定義它的地方描睦。

然后43行 new Observer 的時(shí)候會(huì)走到第一行Observer(關(guān)鍵函數(shù))膊存,打印1。我們發(fā)現(xiàn)Observer實(shí)際就是給data數(shù)據(jù)都添加上get和set方法忱叭,只不過(guò)不添加的方法defineReactive給抽離出去了隔崎。

然后走到第9行,執(zhí)行defineReactive韵丑,打印2,然后15行給每個(gè)屬性加上get和set方法爵卒。

然后走到12行,new Dep的時(shí)候撵彻,會(huì)走到95行執(zhí)行Dep钓株,打印13。Dep函數(shù)剩下的代碼都只是定義函數(shù)陌僵,都不會(huì)執(zhí)行轴合,會(huì)跳出Dep函數(shù)。然后會(huì)到defineReactive函數(shù)第13行碗短,defineReactive剩下的代碼中的函數(shù)也不會(huì)執(zhí)行受葛,所以會(huì)回到Observer,再回到67行,即new Vue的過(guò)程走完了总滩。

然后走到135行的vue.mount()纲堵,走到56行,打印8咳秉。

然后執(zhí)行new Watcher走到70行婉支,打印10。

72行到88行只是定義澜建,沒(méi)有執(zhí)行向挖。89行this.value = fn()中:fn實(shí)際是傳進(jìn)來(lái)的render函數(shù)(看57行),然后后面又加了()就會(huì)立即執(zhí)行炕舵。然后走到60行的render函數(shù)何之,打印9。Watcher就執(zhí)行完了,然后vue.mount()也執(zhí)行完了咽筋。

接著會(huì)走到136行溶推,vue._data.text = '123',注意:這里的vue._data是獲取奸攻,后面的=才是改變值蒜危,所以會(huì)先走get,然后再走set睹耐。所以會(huì)走到21行辐赞,打印10。

然后走到25行硝训,執(zhí)行dep.depend()响委,再走到104行,打印14窖梁。

這時(shí)候判斷Dep.target赘风,這時(shí)候是存在的,所以打印15纵刘。

然后走到110行邀窃,再跳到77行,打印11假哎。

79行執(zhí)行后會(huì)跳到114行蛔翅,打印16。

然后就是賦值操作了位谋,這時(shí)候會(huì)走到28行的set山析,打印4。

繼續(xù)向下走掏父,到36行笋轨,dep.notify(),然后走到119行,打印17爵政。

然后會(huì)走到122行仅讽,觸發(fā)update,走到82行钾挟,打印12洁灵。

然后執(zhí)行fn(),即render函數(shù)掺出,走到60行徽千,打印9。

然后走到63行汤锨,取data值双抽,會(huì)走get,走到21闲礼,打印3牍汹。

然后25行,會(huì)跳到104行柬泽,打印14慎菲。Dep.target為null,15不會(huì)打印

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锨并,一起剝皮案震驚了整個(gè)濱河市露该,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳疏,老刑警劉巖有决,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闸拿,死亡現(xiàn)場(chǎng)離奇詭異空盼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)新荤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門揽趾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人苛骨,你說(shuō)我怎么就攤上這事篱瞎。” “怎么了痒芝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵俐筋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我严衬,道長(zhǎng)澄者,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮粱挡,結(jié)果婚禮上赠幕,老公的妹妹穿的比我還像新娘。我一直安慰自己询筏,他們只是感情好榕堰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著嫌套,像睡著了一般逆屡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灌危,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天康二,我揣著相機(jī)與錄音,去河邊找鬼勇蝙。 笑死沫勿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的味混。 我是一名探鬼主播产雹,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翁锡!你這毒婦竟也來(lái)了蔓挖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馆衔,失蹤者是張志新(化名)和其女友劉穎瘟判,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體角溃,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拷获,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了减细。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匆瓜。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖未蝌,靈堂內(nèi)的尸體忽然破棺而出驮吱,到底是詐尸還是另有隱情,我是刑警寧澤萧吠,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布左冬,位于F島的核電站,受9級(jí)特大地震影響纸型,放射性物質(zhì)發(fā)生泄漏拇砰。R本人自食惡果不足惜九昧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毕匀。 院中可真熱鬧铸鹰,春花似錦、人聲如沸皂岔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)躁垛。三九已至剖毯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間教馆,已是汗流浹背逊谋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留土铺,地道東北人胶滋。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悲敷,于是被迫代替她去往敵國(guó)和親究恤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容