vue原始碼之Array

2021-10-01 15:39:39 字數 4784 閱讀 9049

目錄

響應式具體實現

陣列子集和新增元素的追蹤

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

這一定會觸發listgetter屬性

所以我們依然在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] = 2this.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...