目錄
響應式具體實現
陣列子集和新增元素的追蹤
array中的問題
object通過setter改變屬性的值,所以我們利用getter時傳送依賴收集,在setter時觸發依賴更新,而且vue將資料轉換成響應式資料是在資料初始化時,對object中之後的屬性新增和刪除操作,無法做到自動更新,而是通過vm.set
和vm.
set和vm.
set和vm
.delete手動轉換成響應式,並立即發出更新通知。
但是,一般在對陣列的操作中,可以改變陣列自身內容的方法有push、pop、shift、unshift、splice、sort、reverse七個。當我們給乙個陣列型別的屬性賦值時,屬性的setter函式會觸發,從而通知更新。但是在使用push等一系列操作方法時,由於es6之前,js沒有元程式設計能力,沒有提供可以攔截原型方法的能力,所以,我們思考,如果能在使用者使用這些方法運算元組時得到通知,那就達到了追蹤的目的。
如何做到在操作這些原型方法時能得到通知呢?
對!就是攔截原型。
基本原理:用乙個***覆蓋array.prototype, 每當使用原型上的方法運算元組時,實際上執行的都是***提供的方法,在***中除了呼叫原生的方法運算元組外,還可以幹點別的事,比如:通知依賴更新!
建立***
編寫***
var arrayproto = array.prototype;
// 建立乙個新的空物件arraymethods,並將原型指向array.prototype
var arraymethods = object.
create
(arrayproto)
;var methodstopatch =
['push'
,'pop'
,'shift'
,'unshift'
,'splice'
,'sort'
,'reverse'
];
methodstopatch.
foreach
(function
(method));
});function
def(obj, key, val, enumerable));
}
攔截array.prototype
攔截實質上運用了js中[[prototype]]
機制,在物件自身上查不到屬性和方法引用時,引擎就會繼續在[[prototype]]
關聯的物件上進行查詢,直到頂層array.prototype
。
so! 我們可以在array型別的資料上定義這些方法,從而攔截了使用array.prototype
上的原生方法。然而為每個需要追蹤的資料都新增這七個方法,實在是繁瑣。但是,我們有了原型鏈查詢這種思想,可以輕鬆的實現委託。
即:可以通過將資料的原型(可以通過__proto__
訪問)直接關聯到***物件arraymethods
上,實現攔截。
現在,我們訪問某個陣列(如:list
)的push方法時,它的查詢順序是:
list自身——>arraymethods ——> array.prototype
注意:對於那些不支援__proto__
的瀏覽器,我們只能手動的為每個資料新增方法了。
const hasproto =
'__proto__'
inconst arraykey = object.
getownpropertynames
(arraymethods)
varobserver
=function
observer
(value)
else
// 接下來就要講這個
this
.observearray
(value)
;// 結束
}else};
function
protoaugment
(target, src)
function
copyaugment
(target, src, keys)
}
再說明this.observearray(value);
做了啥之前,我們先搞清楚如何收集跟資料相關的依賴(也就是檢視中對應的坑}
)以及運算元據時是如何通知依賴的呢?
如何收集依賴?
先看下我們訪問乙個陣列的形式,如:
this.list
這一定會觸發list
的getter
屬性
所以我們依然在getter中收集陣列依賴,在***中觸發依賴
依賴收集列表dep
object偵測中,依賴的收集、存放、通知都集中在definereactive函式中。然而,陣列通知依賴必須在***中實現,所以,我們將收集的依賴集合放在observer中,並將每乙個資料例項化的observer掛載到它的__ob__
屬性上。
在***中,通過this.__ob__.dep
就可以訪問所有的依賴。
// this 指向observer例項物件
this
.dep =
newdep()
;def
(value,
'__ob__'
,this
);
__bo__
的作用:
可以在***中訪問observer例項
標記當前value是否被observer轉換成響應式資料
收集依賴
同理,我們通過拿到資料的observer例項,進而拿到dep
列表,進行依賴儲存。
在getter中收集依賴
便於理解,我們舉個例子:
}
// key: list val: [...]
function
definereactive
(obj, key, val)}}
return value
},set
:function
(newval)})
;}function
dependarray
(value)
}}
function
observe
(value)
var ob;
// 判重 如果value具有'__ob__'屬性,說明已經是響應式資料if(
hasown
(value,
'__ob__'
)&& value.__ob__ instanceof
observer
)elseif(
(array.
isarray
(value)
||isplainobject
(value))&&
object.
i***tensible
(value)
&&!value._isvue
)return ob
}
通知依賴
通過前面的贅述,在***中我們可拿到資料的this.__ob__.dep
在***方法重寫**中的佔位符d1處新增依賴通知:
var ob =
this
.__ob__;
ob.dep.
notify()
//向依賴傳送訊息
除了對陣列本身的操作進行追蹤外,還要對陣列的子集(如["a", , 3]
)以及通過push、unshift、splice操作新增的陣列元素進行響應式繫結。
偵測陣列中元素的變化
還記得之前那個this.observearray(value)
嗎?對,它的作用就是迴圈陣列中的每一項,執行 observer函式,來偵測變化。
observer.prototype.
observearray
=function
observearray
(items)
observe 會將資料轉換成響應式的。其實是遞迴呼叫了new observer()
偵測新增元素的變化
思路:拿到新增的元素,並使用observer偵測它
在方法呼叫函式中來拿到新增元素,還記得***方法重寫**中的佔位符d1嗎?在這裡新增:
var ob =
this
.__ob__;
var inserted;
switch
(method)
if(inserted)
同理,拿到新增元素後,呼叫observearray方法進行響應式繫結。
正式由於array的響應式是通過攔截原型方式實現的,所以對於陣列的某些操作,vue是攔截不到的。
例如:this.list[0] = 2
this.list.length = 0
在未來的vue中,可能的解決方案是proxy。
Vue原始碼之createElement函式(五)
在render 函式中,最後呼叫的是createelement函式來返回vnode,那麼createelement函式到底完成了什麼功能 1.首先看一下vnode的定義 src core vdom vnode.js vnode被定義為乙個類。2.在createelement中,首先檢測data的型別...
Vue 原始碼之 nextTick 解析
最近在看 vue 原始碼,一直很好奇這個 nexttick 怎麼實現。本文涉及微任務和巨集任務,不熟悉的可以看我之前的部落格 在瀏覽器環境中,常見的 macro task 有 settimeout messagechannel postmessage setimmediate。而常見的 micro ...
Vue原始碼學習之initEvents
vue原始碼學習之initevents initlifecycle是vue原始碼中core instance events.js下的乙個函式,和上節的initliftcycle一樣,該函式也是在beforecreate鉤子之前呼叫,作用是初始化元件中的事件。下面讓我們來進行 分析。1 initeve...