您好,登錄后才能下訂單哦!
這篇文章主要講解了“Vue2響應式系統有什么用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Vue2響應式系統有什么用”吧!
回到最簡單的代碼:
data = { text: 'hello, world' } const updateComponent = () => { console.log('收到', data.text); } updateComponent() data.text = 'hello, liang' // 運行結果 // 收到 hello, world
響應式系統要做的事情:某個依賴了 數據的函數,當所依賴的 數據改變的時候,該函數要重新執行。data
data
我們期望的效果:當上邊 修改的時候, 函數再執行一次。data.text
updateComponent
為了實現響應式系統,我們需要做兩件事情:
知道 中的數據被哪些函數依賴data
data
中的數據改變的時候去調用依賴它的函數們
為了實現第 點,我們需要在執行函數的時候,將當前函數保存起來,然后在讀取數據的時候將該函數保存到當前數據中。1
第 點就迎刃而解了,當修改數據的時候將保存起來的函數執行一次即可。2
在讀取數據和修改數據的時候需要做額外的事情,我們可以通過 重寫對象屬性的 和 函數。Object.defineProperty()
get
set
我們來寫一個函數,重寫屬性的 和 函數。get
set
/** * Define a reactive property on an Object. */ export function defineReactive(obj, key, val) { const property = Object.getOwnPropertyDescriptor(obj, key); // 讀取用戶可能自己定義了的 get、set const getter = property && property.get; const setter = property && property.set; // val 沒有傳進來話進行手動賦值 if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; /*********************************************/ // 1.這里需要去保存當前在執行的函數 /*********************************************/ return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (setter) { setter.call(obj, newVal); } else { val = newVal; } /*********************************************/ // 2.將依賴當前數據依賴的函數執行 /*********************************************/ }, }); }
為了調用更方便,我們把第 步和第 步的操作封裝一個 類。1
2
Dep
export default class Dep { static target; //當前在執行的函數 subs; // 依賴的函數 constructor() { this.subs = []; // 保存所有需要執行的函數 } addSub(sub) { this.subs.push(sub); } depend() { // 觸發 get 的時候走到這里 if (Dep.target) { // 委托給 Dep.target 去調用 addSub Dep.target.addDep(this); } } notify() { for (let i = 0, l = this.subs.length; i < l; i++) { this.subs[i].update(); } } } Dep.target = null; // 靜態變量,全局唯一
我們將當前執行的函數保存到 類的 變量上。Dep
target
為了保存當前的函數,我們還需要寫一個 類,將需要執行的函數傳入,保存到 類中的 屬性中,然后交由 類負責執行。Watcher
Watcher
getter
Watcher
這樣在 類中, 中保存的就不是當前函數了,而是持有當前函數的 對象。Dep
subs
Watcher
import Dep from "./dep"; export default class Watcher { constructor(Fn) { this.getter = Fn; this.get(); } /** * Evaluate the getter, and re-collect dependencies. */ get() { Dep.target = this; // 保存包裝了當前正在執行的函數的 Watcher let value; try { // 調用當前傳進來的函數,觸發對象屬性的 get value = this.getter.call(); } catch (e) { throw e; } return value; } /** * Add a dependency to this directive. */ addDep(dep) { // 觸發 get 后會走到這里,收集當前依賴 // 當前正在執行的函數的 Watcher 保存到 dep 中的 subs 中 dep.addSub(this); } /** * Subscriber interface. * Will be called when a dependency changes. */ // 修改對象屬性值的時候觸發 set,走到這里 update() { this.run(); } /** * Scheduler job interface. * Will be called by the scheduler. */ run() { this.get(); } }
Watcher
的作用就是將正在執行的函數通過 包裝后保存到 中,然后調用傳進來的函數,此時觸發對象屬性的 函數,會收集當前 。Watcher
Dep.target
get
Watcher
如果未來修改對象屬性的值,會觸發對象屬性的 ,接著就會調用之前收集到的 對象,通過 對象的 方法,來調用最初執行的函數。set
Watcher
Watcher
uptate
回到我們之前沒寫完的 函數,按照上邊的思路,我們來補全一下。defineReactive
import Dep from "./dep"; /** * Define a reactive property on an Object. */ export function defineReactive(obj, key, val) { const property = Object.getOwnPropertyDescriptor(obj, key); // 讀取用戶可能自己定義了的 get、set const getter = property && property.get; const setter = property && property.set; // val 沒有傳進來話進行手動賦值 if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } /*********************************************/ const dep = new Dep(); // 持有一個 Dep 對象,用來保存所有依賴于該變量的 Watcher /*********************************************/ Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; /*********************************************/ // 1.這里需要去保存當前在執行的函數 if (Dep.target) { dep.depend(); } /*********************************************/ return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (setter) { setter.call(obj, newVal); } else { val = newVal; } /*********************************************/ // 2.將依賴當前數據依賴的函數執行 dep.notify(); /*********************************************/ }, }); }
我們再寫一個 方法,把對象的全部屬性都變成響應式的。Observer
export class Observer { constructor(value) { this.walk(value); } /** * 遍歷對象所有的屬性,調用 defineReactive * 攔截對象屬性的 get 和 set 方法 */ walk(obj) { const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]); } } }
我們提供一個 方法來負責創建 對象。observe
Observer
export function observe(value) { let ob = new Observer(value); return ob; }
將上邊的方法引入到文章最開頭的例子,來執行一下:
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: "hello, world", }; // 將數據變成響應式的 observe(data); const updateComponent = () => { console.log("收到", data.text); }; // 當前函數由 Watcher 進行執行 new Watcher(updateComponent); data.text = "hello, liang";
此時就會輸出兩次了~
收到 hello, world
收到 hello, liang
說明我們的響應式系統成功了。
感謝各位的閱讀,以上就是“Vue2響應式系統有什么用”的內容了,經過本文的學習后,相信大家對Vue2響應式系統有什么用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。