最近看了一篇文章講的是vue響應式的實現原理,複述一下吧~

咖啡泡沫發表於2018-12-03

文章參考:mp.weixin.qq.com/s/v8eT3WGol… 或許還沒有原文說的清楚,可能有理解錯的地方,歡迎大佬路過敲打~ 只是單純按照原文打了一遍程式碼o(╥﹏╥)o

想以買東西為模型來進行響應式,想達到的目標是改變數量總價可以隨之改變

/**
 * @price 單價
 * @num 數量
 * @total 總價
 * 
 */
複製程式碼

1.完全不能實現目標

let price = 2;
let num = 3;
let total = price * num;
console.log(total, 'total1')
num = 5;
console.log(total, 'total2')
複製程式碼

2.有點希望,先將有關的變化的值的操作(target函式)存到一個陣列中(storage),每當相關變數變化時(數量變化時)手動呼叫

let price = 2;
let num = 3;
let total = 0;
let target = null;
let storage = []; // 記錄標記函式的

function record() {
    storage.push(target);
}
function replay() {
    storage.forEach(run => run())
}
target = () => {
    total = price * num;
}
record()
replay()
console.log(total, 'total1')
num = 5;
replay()
console.log(total, 'total2')
複製程式碼

我們可以根據需要繼續記錄目標, 但是有一個更強大的解決方案可以擴充套件我們的應用程式。 那就是一個負責維護目標列表的類,當我們需要它們重新執行時, 這些目標列表會得到通知。 解決方法: 使用Class 我們可以開始解決這個問題的一種方法是將這種行為封裝到它自己的Class中,這是一個實現標準程式設計觀察者模式的依賴類。

3.雖然實現了觀察者的類,但是還是需要手動執行訂閱函式,而且在類內還有著外部變數,這是程式設計師所不能忍的

class Dep {
    constructor() { 
        this.subscribers = []; // 儲存變化值操作的函式
    }
    depend() { // 依賴
        this.subscribers.push(target) // 將操作函式壓入陣列
    }
    notify() { // 通知
        this.subscribers.forEach(sub => sub()) // 在接收到通知時 執行操作函式
    }
}
const dep = new Dep();
let price = 2;
let num = 3;
let total = 0;
target = () => {
    total = price * num;
}
dep.depend()
dep.notify()
console.log(total, 'total1')
num = 5;
dep.notify()
console.log(total, 'total2')
複製程式碼

將外部變數傳參進入函式中封裝起來 但是還是留下了全域性變數target來進行資訊的傳遞 wacher中進行賦值 並且呼叫dep來進行儲存 依舊是手動觸發

class Dep {
   constructor() { 
       this.subscribers = []; // 儲存變化值操作的函式
   }
   depend() { // 依賴
       this.subscribers.push(target) // 將操作函式壓入陣列
   }
   notify() { // 通知
       this.subscribers.forEach(sub => sub()) // 在接收到通知時 執行操作函式
   }
}
const dep = new Dep();
let price = 2;
let num = 3;
let total = 0;
function wacher(myFunction) {
   target = myFunction;
   dep.depend();
   target();
   target = null;
}
wacher(() => {
   total = price * num;
})
console.log(total, 'total1')
num = 7;
dep.notify()
console.log(total, 'total2')
複製程式碼

現在只有一個Dep類,但我們真正想要的是每個變數都有自己的Dep

let data = {
    price: 2,
    num: 5
}
let temp = data.num;
Object.defineProperty(data, 'num', { // defineProperty使用方法
    get() {
        console.log('getNum');
        return temp;
    },
    set(val) {
        console.log('setNum');
        temp = val;
    }
})
複製程式碼

對物件中的所有資料執行遞迴加set/get 所以找到了可以在每個變數里加dep的方法

Object.keys(data).forEach(key => {
   let temp = data[key];
   Object.defineProperty(data, key, {
       get() {
           console.log(`get${key}`)
           return temp
       },
       set(val) {
           console.log(`set${key}`)
           temp = val
       }
   })
});
複製程式碼

4.實現了一個簡易版的資料跟蹤

let data = {
   price: 2,
   num: 5
}
let total = 0
let target = null
class Dep {
   constructor() {
       this.subscribers = []
   }
   depend() {
       if(target && !this.subscribers.includes(target)) {
           this.subscribers.push(target)
       }
   }
   notify() {
       this.subscribers.forEach(sub => sub())
   }
}
Object.keys(data).forEach(key => {
   let temp = data[key]
   const dep = new Dep()

   Object.defineProperty(data, key, {
       get() {
           dep.depend()
           return temp
       },
       set(val) {
           temp = val
           dep.notify()
       }
   })

})
function wacher(myFunction) {
   target = myFunction
   target()
   target = null
}
wacher(() => {
   total = data.price * data.num
})
console.log(total, 'total1')
data.num = 88
console.log(total, 'total2')
data.price = 77
console.log(total, 'total3')
複製程式碼

相關文章