現代前端框架的核心組成部分之一是響應式系統。它們是讓應用實現高交互性、動態性和響應性的魔法棒。理解響應式系統是什麼以及如何在實踐中應用它,對於每個 Web 開發人員來說都是一項至關重要的技能。
響應式系統是一種機制,它自動地將數據源(模型)與數據表示(視圖)層保持同步。每當模型發生變化時,視圖都會重新渲染以反映這些變化。
讓我們以一個簡單的 Markdown 編輯器為例。它通常有兩個窗格:一個用於編寫 Markdown 代碼(修改底層模型),另一個用於預覽編譯後的 HTML(顯示更新後的視圖)。當您在編寫窗格中編寫內容時,它會立即且自動地在預覽窗格中預覽。當然,這只是一個簡單的例子。通常情況要復雜得多。
在許多情況下,我們要顯示的數據取決於其他一些數據。在這種情況下,會跟踪依賴項並相應地更新數據。例如,假設我們有一個 fullName 屬性,它取決於 firstName 和 lastName 屬性。當任何依賴項被修改時,fullName 屬性會自動重新計算,結果會顯示在視圖中。
既然我們已經確定了什麼是響應式,那麼現在是時候學習新的 Vue 3 響應式機制的工作原理以及如何在實踐中使用它了。但在我們這樣做之前,我們將快速瀏覽一下舊的 Vue 2 響應式機制及其缺點。
reactive
、ref
和 readonly
方法允許開發人員更精細地控制數據響應性,其中 ref
用於基本類型,reactive
用於對象。 computed
和 watch
)為開發人員提供了工具,通過有效地管理依賴項和副作用來創建更動態和響應的應用程序。 Vue 2 中的響應式機製或多或少是“隱藏的”。我們將任何內容放入 data 對像中,Vue 都會隱式地使其具有響應性。一方面,這簡化了開發人員的工作,但另一方面,它也導致靈活性降低。
在幕後,Vue 2 使用 ES5 Object.defineProperty() 將 data 對象的所有屬性轉換為getter和setter。對於每個組件實例,Vue 創建一個依賴項觀察器實例。在組件渲染期間作為依賴項收集/跟踪的任何屬性都會由觀察器記錄。稍後,當依賴項的 setter 被觸發時,觀察器會收到通知,組件會重新渲染並更新視圖。這基本上就是所有魔法的工作原理。不幸的是,有一些需要注意的地方。
由於 Object.defineProperty() 的限制,Vue 無法檢測某些數據更改。這些包括:
幸運的是,為了處理這些限制,Vue 為我們提供了 Vue.set API 方法,該方法向響應式對象添加一個屬性,確保新屬性也具有響應性,從而觸發視圖更新。
讓我們在下面的示例中探索上述情況:
<div id="app"> <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="(item, index) in activities" :key="index">{{ item }} <button @click="editActivity(index)">Edit</button></li> </ul> <button @click="clearActivities">Clear the activities list</button> </div>
const App = new Vue({ el: '#app', data: { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] }, methods: { // 1. Add a new property to an object addAgeProperty() { this.person.age = 30 }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { this.activities[index] = newValue } }, // 3. Modifying the length of the array clearActivities() { this.activities.length = 0 } } });
在上面的示例中,我們可以看到這三種方法都不起作用。我們無法向 person 對象添加新屬性。我們無法使用索引編輯 activities 數組中的項目。我們也無法修改 activities 數組的長度。
當然,這些情況有解決方法,我們將在下一個示例中探討:
const App = new Vue({ el: '#app', data: { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] }, methods: { // 1. Adding a new property to the object addAgeProperty() { Vue.set(this.person, 'age', 30) }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { Vue.set(this.activities, index, newValue) } }, // 3. Modifying the length of the array clearActivities() { this.activities.splice(0) } } });
在這個示例中,我們使用 Vue.set API 方法向 person 對象添加新的 age 屬性,並從 activities 數組中選擇/修改特定項目。在最後一種情況下,我們只使用 JavaScript 內置的 splice() 數組方法。
如我們所見,這有效,但這有點笨拙,並導致代碼庫不一致。幸運的是,在 Vue 3 中,這個問題已得到解決。讓我們在下面的示例中看看它的神奇之處:
const App = { data() { return { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] } }, methods: { // 1. Adding a new property to the object addAgeProperty() { this.person.age = 30 }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { this.activities[index] = newValue } }, // 3. Modifying the length of the array clearActivities() { this.activities.length = 0 } } } Vue.createApp(App).mount('#app')
在這個使用 Vue 3 的示例中,我們恢復到第一個示例中使用的內置 JavaScript 功能,現在所有方法都能正常工作。
在 Vue 2.6 中,引入了 Vue.observable() API 方法。它在某種程度上公開了響應式系統,允許開發人員顯式地使對象具有響應性。實際上,這與 Vue 在內部包裝 data 對象所使用的確切方法相同,對於為簡單場景創建最小的跨組件狀態存儲很有用。但是,儘管它很有用,但這種單一方法無法與 Vue 3 附帶的完整、功能豐富的響應式 API 的功能和靈活性相匹配。我們將在接下來的部分中看到原因。
注意:因為 Object.defineProperty() 只是一個 ES5 功能且無法模擬,所以 Vue 2 不支持 IE8 及以下版本。
Vue 3 中的響應式系統已完全重寫,以便利用 ES6 Proxy 和 Reflect API。新版本公開了一個功能豐富的響應式 API,使系統比以前更靈活、更強大。
Proxy API 允許開發人員攔截和修改目標對像上的低級對像操作。代理是對象的克隆/包裝器(稱為目標),並提供特殊函數(稱為陷阱),這些函數響應特定操作並覆蓋 JavaScript 對象的內置行為。如果您仍然需要使用默認行為,則可以使用相應的 Reflection API,其方法顧名思義,反映了 Proxy API 的方法。讓我們探索一個示例,看看這些 API 如何在 Vue 3 中使用:
<div id="app"> <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="(item, index) in activities" :key="index">{{ item }} <button @click="editActivity(index)">Edit</button></li> </ul> <button @click="clearActivities">Clear the activities list</button> </div>
要創建一個新的代理,我們使用 new Proxy(target, handler) 構造函數。它接受兩個參數:目標對象(person 對象)和處理程序對象,該對象定義將攔截哪些操作(get 和 set 操作)。在處理程序對像中,我們使用 get 和 set 陷阱來跟踪何時讀取屬性以及何時修改/添加屬性。我們設置控制台語句以確保方法正常工作。
get 和 set 陷阱採用以下參數:
Reflect API 方法接受與其對應的代理方法相同的參數。它們用於為給定操作實現默認行為,對於 get 陷阱是返回屬性名稱,對於 set 陷阱是如果設置了屬性則返回 true,否則返回 false。
註釋的 track() 和 trigger() 函數特定於 Vue,用於跟踪何時讀取屬性以及何時修改/添加屬性。結果,Vue 會重新運行使用該屬性的代碼。
在示例的最後一部分,我們使用控制台語句輸出原始 person 對象。然後,我們使用另一個語句讀取代理對象的屬性名稱。接下來,我們修改 age 屬性並創建一個新的 hobby 屬性。最後,我們再次輸出 person 對像以查看它是否已正確更新。
這就是 Vue 3 響應式機制的簡要說明。當然,實際實現要復雜得多,但希望上面提供的示例足以讓您掌握主要思想。
使用 Vue 3 響應式機制時,還需要考慮以下幾點:
最後,我們進入 Vue 3 響應式 API 本身。在以下部分,我們將探索按邏輯分組的 API 方法。我把方法分組是因為我認為以這種方式呈現更容易記住。讓我們從基礎開始。
第一組包括用於控制數據響應性的最基本方法:
現在讓我們看看這些方法的實際應用:
<div id="app"> <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="(item, index) in activities" :key="index">{{ item }} <button @click="editActivity(index)">Edit</button></li> </ul> <button @click="clearActivities">Clear the activities list</button> </div>
在這個示例中,我們探索了四種基本響應式方法的使用。
首先,我們創建一個值為 0 的 counter ref 對象。然後,在視圖中,我們放置兩個按鈕,分別遞增和遞減 counter 的值。當我們使用這些按鈕時,我們看到 counter 確實是響應式的。
其次,我們創建一個 person 響應式對象。然後,在視圖中,我們放置兩個輸入控件,分別用於編輯人的姓名和年齡。當我們編輯人的屬性時,它們會立即更新。
第三,我們創建一個 math 只讀對象。然後,在視圖中,我們設置一個按鈕來使 math 的 PI 屬性的值加倍。但是當我們單擊按鈕時,控制台中會顯示一條錯誤消息,告訴我們該對像是只讀的,我們無法修改其屬性。
最後,我們創建一個 alphabetNumbers 對象,我們不想將其轉換為代理,並將其標記為原始對象。它包含所有字母及其對應的數字(為簡潔起見,此處僅使用前三個字母)。此順序不太可能更改,因此我們故意將此對象保持為普通對象,這有利於性能。我們在表格中呈現對象內容,並設置一個按鈕來將 B 屬性的值更改為 3。我們這樣做是為了表明,儘管對象可以被修改,但這不會導致視圖重新渲染。
markRaw 非常適合我們不需要使其具有響應性的對象,例如很長的國家代碼列表、顏色名稱及其對應的十六進制數字等等。
最後,我們使用下一節中描述的類型檢查方法來測試和確定我們創建的每個對象的類型。我們使用 onMounted() 生命週期鉤子在應用程序最初呈現時觸發這些檢查。
此組包含上面提到的所有四個類型檢查器:
此組包含其他 ref 方法:
此組中的方法是 ref、reactive 和 readonly 的“淺層”等效項:
讓我們通過檢查以下示例來更容易地理解這些方法:
<div id="app"> <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="(item, index) in activities" :key="index">{{ item }} <button @click="editActivity(index)">Edit</button></li> </ul> <button @click="clearActivities">Clear the activities list</button> </div>
這個示例從創建一個 settings shallow ref 對像開始。然後,在視圖中,我們添加兩個輸入控件來編輯其 width 和 height 屬性。但是當我們嘗試修改它們時,我們看到它們沒有更新。為了解決這個問題,我們添加一個按鈕,它會更改整個對象及其所有屬性。現在它可以工作了。這是因為 value 的內容(width 和 height 作為單個屬性)沒有轉換為響應式對象,但 value 的變異(整個對象)仍然被跟踪。
接下來,我們創建一個 settingsA shallow reactive 代理,它包含 width 和 height 屬性以及一個嵌套的 coords 對象,其中包含 x 和 y 屬性。然後,在視圖中,我們為每個屬性設置一個輸入控件。當我們修改 width 和 height 屬性時,我們看到它們會響應式地更新。但是當我們嘗試修改 x 和 y 屬性時,我們看到它們沒有被跟踪。
最後,我們創建一個 settingsB shallow readonly 對象,它與 settingsA 具有相同的屬性。在這裡,當我們嘗試修改 width 或 height 屬性時,控制台中會顯示一條錯誤消息,告訴我們該對像是只讀的,我們無法修改其屬性。另一方面,可以毫無問題地修改 x 和 y 屬性。
來自最後兩個示例的嵌套 coords 對像不受轉換的影響,並且保持為普通對象。這意味著它可以自由修改,但它的任何修改都不會被 Vue 跟踪。
接下來的三種方法用於將代理轉換為 ref(s) 或普通對象:
讓我們看看這些轉換如何在下面的示例中工作:
const App = new Vue({ el: '#app', data: { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] }, methods: { // 1. Add a new property to an object addAgeProperty() { this.person.age = 30 }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { this.activities[index] = newValue } }, // 3. Modifying the length of the array clearActivities() { this.activities.length = 0 } } });
在這個示例中,我們首先創建一個基本 person 響應式對象,我們將將其用作源對象。
然後,我們將 person 的 name 屬性轉換為具有相同名稱的 ref。然後,在視圖中,我們添加兩個輸入控件——一個用於 name ref,另一個用於 person 的 name 屬性。當我們修改其中一個時,另一個也會相應地更新,因此它們之間的響應式連接得以保持。
接下來,我們將 person 的所有屬性轉換為包含在 personDetails 對像中的單個 ref。然後,在視圖中,我們再次添加兩個輸入控件來測試我們剛剛創建的一個 ref。如我們所見,personDetails 的 age 與 person 的 age 屬性完全同步,就像在前面的示例中一樣。
最後,我們將 person 響應式對象轉換為 rawPerson 普通對象。然後,在視圖中,我們添加一個輸入控件來編輯 rawPerson 的 hobby 屬性。但是如我們所見,Vue 不會跟踪轉換後的對象。
最後一組方法用於計算複雜值和“監視”特定值:
讓我們考慮以下示例:
<div id="app"> <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="(item, index) in activities" :key="index">{{ item }} <button @click="editActivity(index)">Edit</button></li> </ul> <button @click="clearActivities">Clear the activities list</button> </div>
在這個示例中,我們創建一個 fullName computed 變量,它基於 firstName 和 lastName ref 進行計算。然後,在視圖中,我們添加兩個輸入控件來編輯全名的兩個部分。如我們所見,當我們修改任何一個部分時,fullName 會重新計算,結果也會更新。
接下來,我們創建一個 volume ref 並為其設置一個 watch effect。每次修改 volume 時,effect 都會運行回調函數。為了證明這一點,在視圖中,我們添加一個按鈕,將 volume 遞增 1。我們在回調函數中設置一個條件,測試 volume 的值是否可以被 3 整除,當它返回 true 時,會顯示一個警報消息。 effect 在應用程序啟動時運行一次,並設置 volume 的值,然後在每次修改 volume 的值時再次運行。
最後,我們創建一個 state ref 並設置一個 watch 函數來跟踪它的更改。一旦 state 發生更改,回調函數就會執行。在這個示例中,我們添加一個按鈕,在 playing 和 paused 之間切換 state。每次發生這種情況時,都會顯示一條警報消息。
watchEffect 和 watch 在功能方面看起來非常相似,但它們有一些明顯的區別:
如您所見,Vue 3 響應式 API 提供了大量方法,可用於各種用例。 API 非常龐大,在本教程中,我只探討了基礎知識。有關更深入的探索、細節和邊緣情況,請訪問響應式 API 文檔。
在本文中,我們介紹了什麼是響應式系統以及它如何在 Vue 2 和 Vue 3 中實現。我們看到 Vue 2 有一些缺點在 Vue 3 中得到了成功解決。 Vue 3 響應式機制是基於現代 JavaScript 功能的完整重寫。讓我們總結一下它的優缺點。
優點:
缺點:
底線是 Vue 3 響應式機制是一個靈活且強大的系統,Vue 和非 Vue 開發人員都可以使用它。無論您的情況如何,只需抓住它並開始構建令人驚嘆的東西即可。
Vue 3 響應式系統是 Vue.js(一個流行的 JavaScript 框架)的一個基本方面。它負責跟踪應用程序狀態中的更改並更新 DOM(文檔對像模型)以反映這些更改。該系統圍繞“響應式對象”的概念構建。當這些對象的屬性發生更改時,Vue 會自動觸發必要的更新。這使開發人員更容易構建動態、響應式的 Web 應用程序。
Vue 3 的響應式系統已使用 JavaScript 的 Proxy 對象完全重寫,與 Vue 2 的 Object.defineProperty 方法相比,它提供了一種更高效、更強大的跟踪更改的方式。這個新系統允許 Vue 跟踪嵌套屬性的更改,這是 Vue 2 的一個限制。它還減少了內存佔用並提高了性能。
要使用 Vue 3 響應式系統,您需要使用 reactive() 函數包裝您的數據。此函數使對象及其屬性具有響應性。當屬性更改時,Vue 將自動重新渲染依賴於它的組件。您還可以使用 ref() 函數使單個變量具有響應性。
Vue 3 中的 ref() 函數用於創建對值的響應式引用。它將值包裝在一個具有單個屬性“.value”的對像中,並使此對象具有響應性。這意味著當值更改時,使用此 ref 的任何組件都將更新。
reactive() 和 ref() 都用於在 Vue 3 中使數據具有響應性,但它們用於不同的場景。 reactive() 函數用於使對象具有響應性,而 ref() 函數用於使基本值(如字符串或數字)具有響應性。但是,ref() 也可以與對像一起使用,在這種情況下,它的行為類似於 reactive()。
Vue 3 處理數組的響應式方式與處理對象的方式相同。如果您使用 reactive() 函數使數組具有響應性,Vue 將跟踪對數組元素及其長度的更改。這意味著如果您添加、刪除或替換元素,Vue 將更新依賴於該數組的組件。
Vue 3 中的 toRefs() 函數用於將響應式對象轉換為普通對象,其中原始對象的每個屬性都表示為一個 ref。當您想要解構響應式對象但仍保持其響應性時,這很有用。
您可以使用 markRaw() 函數來防止對象具有響應性。這在某些情況下很有用,在這些情況下,您不希望 Vue 跟踪對對象的更改。
Vue 3 中的 computed() 函數用於創建一個依賴於其他響應式屬性的響應式屬性。當任何依賴項發生更改時,computed 屬性的值會自動更新。這對於計算或轉換非常有用,這些計算或轉換應在底層數據更改時更新。
Vue 3 的響應式系統完全支持 JavaScript 的 Map 和 Set 數據結構。如果您使 Map 或 Set 具有響應性,Vue 將分別跟踪其條目或元素的更改。這意味著如果您添加、刪除或替換條目或元素,Vue 將更新依賴於 Map 或 Set 的組件。
以上是了解VUE 3中的新反應性系統的詳細內容。更多資訊請關注PHP中文網其他相關文章!