數(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ì)打印