以前大概瞭解了 響應式原理、讓普通屬性變成響應式數據的 ref、effect 的實現以及 計算屬性的包裝。javascript
本篇從逆向的角度作個簡單的整理,草草的結束掉寫的很爛的文章。 你能夠看往期的寫的很爛的 你爲何看不懂源碼之Vue 3.0 java
你的需求是開發 reactive
模塊,它是幹什麼的呢?將變量生成響應式數據。react
響應式數據被更改時,須要作一些反應。那怎麼能檢測響應式的變動呢?在談這個問題前,得先羅列下,變量有哪些類型?typescript
String Number Symbol Boolean數組
形如:app
let a = 2
let b = 'lalla'
let c = Symbol('cObj')
複製代碼
形如:函數
let obj = { name: 'obj' }
複製代碼
Map WeakMap Set WeakSet 形如post
let map = new Map([{name: 'map'}])
let set = new Set([1,2,3])
複製代碼
基本上是這三類,但這三類可能交織縱橫,互相「包庇」!我太難了。ui
既然分出了變量的類型,逐一攔截,而後再作「反應」就好了。先從普通變量談起。this
無論是 Object.defineProperty()
仍是 Proxy()
,攔截的都是對象,因此經過這兩種方式來攔截普通變量確定行不通,但咱們能夠包裝它,從而實現攔截。最簡單的方式即是對象的特性「存取器」。
let a = 3
const b = {
get value() {
return a
},
set value(val) {
a = val
}
}
複製代碼
這塊兒的實現即是 Ref.ts
這在 Vue2.0 中已經實現過了,用的 ES5 的 Object.defineProperty
。直接拿來用就好了。
A: 等等,不能用!!
B: 爲何?
A: Object.defineProperty 在攔截對象時,須要遍歷其 key 值。若是對象層級深,還要遞歸的遍歷。
Object.keys(obj).forEach((key) => {
Object.defineProperty(obj, key, handlers)
})
複製代碼
A: 它還攔截不了數組的方法,想攔截還得作 hack,包裝 push\pop......操做。
B: 那咋整
A: Proxy 呀,以上問題它都能迎刃而解,具體咱們不是在往期文章分析過了麼。這裏就不贅述了。
Proxy
這麼牛,集合應該也能攔截呀。不幸的是,集合的各類方法都不能被 Proxy 攔截。 拿 Set 類型舉例子。
const sets = new Set([1,2,3])
let p2 = new Proxy(sets, {
get(target, key ,receiver) {
return Reflect.get(target, key, receiver)
},
add() {}
})
p2.add(6)
複製代碼
當你這樣調用時,會發現控制檯報錯了
Uncaught TypeError: Method Set.prototype.add called on incompatible receiver [object Object]
致使這個報錯的緣由是,在調用 add
時,其內部的 this
本應該是 set
自己,但在此處變爲了 p2
。
那咋整呢,要不,咱們本身實現個 add 方法,在 p2.add
時,用 apply
改變 指針。
const sets = new Set([1,2,3])
let p2 = new Proxy(sets, {
get(target, key ,receiver) {
const fn = Reflect.get(target, key, receiver)
if(typeof fn === 'function') {
return (...val) => {
return target[key].apply(sets, val)
}
}
return fn
}
})
p2.add(6)
複製代碼
真實狀況比這要複雜,你須要考慮各類 traps。
通過以上,目前咱們也能攔截到 collections
了。
響應式數據是不夠的,若是一個數據的變動引發了一堆複雜的邏輯,咱們處理起來就會特別麻煩,計算屬性顯然是個好選擇。
const data = reactive({
name: 'qqqdu'
})
computed((val) => {
return data.name + ' hello'
})
複製代碼
以上方法有兩個場景:
前者好說,要實現 2,就須要創建起 函數與計算屬性的聯繫。這個聯繫在什麼階段創建比較好呢,固然是初始化的時候。
當初始化時,開始調用函數時加鎖,運行到 data.name
會觸發其 Proxy get
,這說明該函數依賴這個計算屬性,那他們之間的聯繫就創建起來了。當函數生命週期結束時,解鎖就能夠了。
那 2,何時執行呢,固然是計算屬性 set
時了,若是值發生了變化,就要遍歷執行與之相關的全部計算函數。
到這裏就能夠了,帶着這個思路再回顧,會清晰很多。
源碼這種東西吧,越看越以爲本身卑微,向大佬們致敬! --xiaodu