Vue.js是如何做到數據響應的?

許多前端JavaScript框架(例如Angular,React和Vue)都有自己的數據相應引擎。通過了解相應性及其工作原理,您可以提高開發技能並更有效地使用JavaScript框架。在視頻和下面的文章中,我們構建了您在Vue源代碼中看到的相同類型的Reactivity。

當你第一次看到它時,Vue的響應系統看起來很神奇。拿這個簡單的Vue應用程序:

不知何故,Vue只知道如果價格發生變化,它應該做三件事:

  • 更新我們網頁上的價格值。
  • 重新計算乘以price * quantity的表達式,並更新頁面。
  • 再次調用totalPriceWithTax函數並更新頁面。

但是等等,你應該會覺得奇怪,當價格變化時,Vue如何知道要更新什麼,以及它如何跟蹤所有內容?

這不是JavaScript編程常規的工作方式。

如果你不明白,那我們試着看看常規的JavaScript是怎麼運行的。例如,如果我運行此代碼:

你覺得它打印什麼?由於我們沒有使用Vue,它將打印10。

在Vue,我們希望每當價格或數量更新時,總計都會得到更新。我們想要:

不幸的是,JavaScript是程序性的,而不是被動的,所以這在現實生活中不起作用。爲了使數據變化得到相應,我們必須使用JavaScript來使事情表現不同。

⚠️ 問題

我們需要保存計算總數的方式,以便在價格或數量變化時重新運行。

✅ 解決方案

首先,我們需要一些方法告訴我們的應用程序,“我即將運行的代碼,存儲它,我可能需要你在另一個時間運行它。”然後我們將要運行代碼,如果價格或數量變量得到更新,再次運行存儲的代碼。

請注意,我們在目標變量中存儲了一個匿名函數,然後調用了一個記錄函數。使用ES6箭頭語法我也可以這樣寫:

請注意,我們在目標變量中存儲了一個匿名函數,然後調用了一個記錄函數。使用ES6箭頭語法我也可以這樣寫:

記錄的方法:

我們正在存儲目標(在我們的例子中是{total = price * quantity}),所以我們可以稍後運行它。

這將遍歷存儲陣列中存儲的所有匿名函數並執行它們中的每一個。

然後在我們的代碼中,我們可以:

很簡單吧?如果您需要閱讀並嘗試再次掌握它,這裏的代碼就完整了。僅供參考,如果您想知道原因,我會以特定的方式對此進行編碼。

⚠️ 問題

我們可以根據需要繼續記錄目標,但是有一個更強大的解決方案可以擴展我們的應用程序。那就是一個負責維護目標列表的類,當我們需要它們重新運行時,這些目標列表會得到通知。

✅ 解決方法: 使用Class

我們可以開始解決這個問題的一種方法是將這種行爲封裝到它自己的Class中,這是一個實現標準編程觀察者模式的依賴類。

因此,如果我們創建一個JavaScript類來管理我們的依賴項(它更接近Vue處理事物的方式),它可能看起來像這樣:

讓它運行:

它仍然有效,現在我們的代碼感覺更可靠了。只有仍然感覺有點奇怪的是target()的設置和運行。

⚠️ 問題

我們將爲每個變量設置一個Dep類,並且很好地封裝了創建需要監視更新的匿名函數的行爲。也許觀察者功能可能是爲了處理這種行爲。

(這只是上面的代碼)

我們可以改爲:

✅ 解決方案:觀察者功能

在我們的Watcher功能中,我們可以做一些簡單的事情:

如您所見,watcher函數接受myFunc參數,將其設置爲我們的全局目標屬性,調用dep.depend()以將目標添加爲訂閱者,調用目標函數並重置目標。

現在,當我們運行以下內容時:

您可能想知道爲什麼我們將target實現爲全局變量,而不是將其傳遞到我們需要的函數中。 這有一個很好的理由,這將在我們的文章結尾處揭曉。

⚠️ 問題

我們有一個Dep類,但我們真正想要的是每個變量都有自己的Dep。在我們繼續之前,先存儲一下數據。

讓我們假設我們的每個屬性(價格和數量)都有自己的內部Dep類。

當我們運行時:

由於訪問了data.price值,我希望price屬性的Dep類將我們的匿名函數(存儲在目標中)推送到其訂閱者數組(通過調用dep.depend())。由於訪問了data.quantity,我還希望quantity屬性Dep類將此匿名函數(存儲在目標中)推送到其訂閱者數組中。

如果我有另一個匿名函數,只訪問data.price,我希望只推送到價格屬性Dep類。

我什麼時候想要在價格訂閱者上調用dep.notify()?我希望在設定價格時調用它們。在文章的最後,我希望能夠進入控制檯並執行:

我們需要一些方法來掛鉤數據屬性(如價格或數量),所以當它被訪問時我們可以將目標保存到我們的訂閱者數組中,當它被更改時,運行存儲在我們的訂閱者數組中的函數。

✅ 解決方案:Object.defineProperty()

我們需要了解Object.defineProperty()函數,它是簡單的ES5 JavaScript。它允許我們爲屬性定義getter和setter函數。在我向您展示如何在Dep類中使用它之前,先簡單展示一下改函數的用法。

如您所見,它只記錄兩行。但是,它實際上並沒有獲取或設置任何值,因爲我們過度使用了該功能。我們現在加回來吧。 get()期望返回一個值,而set()仍然需要更新一個值,所以讓我們添加一個internalValue變量來存儲我們當前的價格值。

既然我們的get和set工作正常,您認爲將打印到控制檯的是什麼?

因此,當我們獲取並設置值時,我們可以獲得通知。通過一些遞歸,我們可以爲數組中的所有項運行它

FYI,Object.keys(data)返回對象鍵的數組。

現在一切都有getter和setter,我們在控制檯上看到了這一點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章