大家好蔫饰,我是 輝夜真是太可愛(ài)啦 。這是我最近在寫(xiě)的【手把手教你搓Vue響應(yīng)式原理】系列愉豺,本文將一步步地為你解開(kāi)vue響應(yīng)式原理的面紗篓吁。由于本人也是在寫(xiě)這篇文章的過(guò)程中不斷試錯(cuò),不斷學(xué)習(xí)改進(jìn)的蚪拦,所以杖剪,本文同樣很適合和我一樣的初學(xué)者。和
Vue
的設(shè)計(jì)理念如出一轍驰贷,那就是漸進(jìn)增強(qiáng)盛嘿。
上文鏈接
【手把手教你搓Vue響應(yīng)式原理】(一)初識(shí)Vue響應(yīng)式
【手把手教你搓Vue響應(yīng)式原理】(二)深度監(jiān)測(cè)對(duì)象全部屬性
前言
在上一文中,我們已經(jīng)成功將深度對(duì)象(好幾層的對(duì)象)成功遍歷括袒,全部綁定了 defineReactive
次兆,也就是全部弄成了響應(yīng)式對(duì)象。
雖然上述功能已經(jīng)基本實(shí)現(xiàn)了對(duì)象的成功遍歷锹锰,但是芥炭,在 Vue 的源碼中,在我們遍歷對(duì)象的同時(shí)恃慧,會(huì)將 new Observer()
創(chuàng)建的實(shí)例賦值到 __ob__
屬性添加到每一個(gè)對(duì)象中园蝠。
這篇文章主要對(duì)標(biāo)源碼,新增了 __ob__
屬性痢士,以及 observe
入口方法彪薛,相當(dāng)于是對(duì)標(biāo)源碼,更規(guī)范地書(shū)寫(xiě)響應(yīng)式良瞧。
這個(gè) __ob__
屬性陪汽,在vue使用中大家也可能有所眼熟,這篇文章本意也在于更規(guī)范地書(shū)寫(xiě)響應(yīng)式褥蚯。
并且挚冤,由于少了一個(gè) observe
入口方法,也是為了上一文中關(guān)于遍歷對(duì)象添加 defineProperty
的寫(xiě)法更方便能被大家理解赞庶。
我們會(huì)將 __ob__
放在 Observer
中训挡,并且,將判斷是否是對(duì)象放在 observe
中歧强,以及是否設(shè)置了 __ob__
屬性澜薄,將 observe
作為新的入口方法。
整體的調(diào)用順序如圖:
上文代碼
上文最終的代碼如下:
function defineReactive(obj,key,val) {
console.log(key);
// 判斷當(dāng)前入?yún)€(gè)數(shù)摊册,兩個(gè)的話直接返回當(dāng)前層的對(duì)象
if(arguments.length===2){
val=obj[key];
typeof val === 'object' && new Observer(val);
}
Object.defineProperty(obj,key,{
// 可枚舉肤京,默認(rèn)為 false
enumerable:true,
// 屬性的描述符能夠被改變,或者是刪除,默認(rèn)為 false
configurable:true,
get(){
return val;
},
set(newValue){
val=newValue;
typeof val === 'object' && new Observer(val);
}
})
}
// 遍歷對(duì)象當(dāng)前層的所有屬性忘分,并且綁定 defineReactive
class Observer{
constructor(obj){
this.walk(obj);
}
walk(obj){
let keys=Object.keys(obj);
for(let i =0;i<keys.length;i++){
defineReactive(obj,keys[i])
}
}
}
__ob__
由于要遍歷添加 __ob__
棋枕, 所以,我們要先改寫(xiě) Observer
類妒峦,在構(gòu)造函數(shù)中重斑,新增對(duì)于 __ob__
屬性的構(gòu)建。
class Observer{
constructor(obj){
Object.defineProperty(obj,'__ob__',{
value:this,
//這個(gè)屬性僅僅保存 Observer 實(shí)例肯骇,所以不需要遍歷
enumerable:false
})
this.walk(obj);
}
// ...
}
當(dāng)然窥浪,我們可以對(duì)上面的代碼進(jìn)行一定的封裝。
function def(obj,key,value,enumerable) {
Object.defineProperty(obj,key,{
value,
enumerable
})
}
// 遍歷對(duì)象當(dāng)前層的所有屬性笛丙,并且綁定 defineReactive
class Observer{
constructor(obj){
def(obj,'__ob__',this,false)
this.walk(obj);
}
// ...
}
observe
既然現(xiàn)在有了 __ob__
屬性漾脂,我們可以根據(jù)它是否有 __ob__
屬性來(lái)判斷它是否 new Observer
實(shí)例化過(guò),而且可以將是否是對(duì)象的判斷放在 observe
方法中若债,將它作為入口文件符相。
現(xiàn)在的執(zhí)行順序應(yīng)該是 observe => new Observe => defineReactive
function observe(value) {
if(typeof value !== 'object') return;
let ob;
// eslint-disable-next-line no-prototype-builtins
if(value.hasOwnProperty('__ob__') && value.__ob__ instanceof Observer){
ob=value.__ob__;
}else{
ob = new Observer(value);
}
return ob;
}
由于我們已經(jīng)將是否對(duì)象的判斷放入了 observe
中,所以蠢琳,需要將之前的 typeof val === 'object' && new Observer(val);
改為 observe(val)
啊终。
所以,最終的代碼如下:
function defineReactive(obj,key,val) {
console.log(key);
// 判斷當(dāng)前入?yún)€(gè)數(shù)傲须,兩個(gè)的話直接返回當(dāng)前層的對(duì)象
if(arguments.length===2){
val=obj[key];
observe(val)
}
Object.defineProperty(obj,key,{
// 可枚舉蓝牲,默認(rèn)為 false
enumerable:true,
// 屬性的描述符能夠被改變,或者是刪除泰讽,默認(rèn)為 false
configurable:true,
get(){
return val;
},
set(newValue){
val=newValue;
observe(val)
}
})
}
function def(obj,key,value,enumerable) {
Object.defineProperty(obj,key,{
value,
//這個(gè)屬性僅僅保存 Observer 實(shí)例例衍,所以不需要遍歷
enumerable
})
}
// 遍歷對(duì)象當(dāng)前層的所有屬性,并且綁定 defineReactive
class Observer{
constructor(obj){
def(obj,'__ob__',this,false)
this.walk(obj);
}
walk(obj){
let keys=Object.keys(obj);
for(let i =0;i<keys.length;i++){
defineReactive(obj,keys[i])
}
}
}
function observe(value) {
if(typeof value !== 'object') return;
let ob;
// eslint-disable-next-line no-prototype-builtins
if(value.hasOwnProperty('__ob__') && value.__ob__ instanceof Observer){
ob=value.__ob__;
}else{
ob = new Observer(value);
}
return ob;
}