前面提到了模板搭配指令及 Mustache 的用法。今天則是要介紹所謂透過getter
跟setter
取值跟賦值的方式在 JavaScript 是怎麼做到的,這部分會跟 Vue 本身的響應式基礎有很大相關。此外,我也會帶著大家理解Options API
在處理響應式資料時的差異。
Vue 3 的響應式系統基於JavaScript
的Proxy
,其核心原理是通過 get 函數
來攔截並取得物件屬性的值,並通過set 函數
來攔截對目標物件 (target) 的修改。這使得 Vue 可以在數據變更時自動更新視圖,實現響應式的雙向綁定效果。
以下示範了如何透過原生 JavaScript Proxy 模擬 Vue 的更新機制:
👉 JavaScript Proxy 物件模擬Vue更新機制實作連結
流程說明
javaScript Proxy
監控({ productName: "apple" }
物件。get 函數
來攔截屬性值的讀取,以及set 函數
來攔截對屬性的修改。set 函數
。JavaScript Proxy
允許攔截對物件的操作,並對這些操作進行自定義處理。通過設置處理器(handler)
,可以監聽物件內部屬性的所有操作,無論是讀取
還是賦值
,從而統一管理。
JavaScript:
// Proxy 處理器
const handler = {
// target:目標對象 property:屬性 receiver:proxy
get: function (target, property, receiver) {
return `${JSON.stringify(target)}-${property}`;
},
// obj:目標對象 prop:屬性 value:取代新值
set(obj, prop, value) {
obj[prop] = value;
}
};
// 渲染函式
const render = () =>{
document.querySelector(".getText").innerHTML = proxy.productName;
}
// proxy 物件初始化
const proxy = new Proxy({ productName: "apple" }, handler);
// 渲染初始化畫面({ productName: "apple" }) -> 定期器三秒觸發 -> 將目標物件值從 apple 更換成 banana -> 重新渲染新值到畫面上
render()
setTimeout(()=>{
proxy.productName = 'banana'
render()
}, 3000)
⭐ 需要注意的是,Proxy 只能應用於物件,不能作用於基礎數據類型(如字符串、數字等),否則會導致錯誤。
還想知道更多關於 JavaScript Proxy ,可以參考 MDN JavaScript Proxy
Vue 的響應式系統基於 Proxy
,並進行了許多優化:
在使用 Vue 的 Options API 時,由於上下文的關係,我們通常需要使用 this 來訪問組件的資料或方法。那麼,這裡的 this 實際上指向的是什麼對象呢?
我們可以通過 Vue Options API 生命週期中的mounted 方法
來舉例說明這個概念。mounted 方法會在 Vue 綁定成功且模板更新完成後觸發,常用來操作 DOM 或進行初始化邏輯。
⭐ mount 函數與 mounted 生命週期掛鉤的區別
Vue 應用實例
掛載到網頁的指定元素上。例如,使用 app.mount('#app') 可以將應用實例掛載到具有 id="app" 的 DOM 元素上。mounted
是 Vue 組件的生命週期方法之一。當組件的模板已經被完全渲染成 DOM 並掛載到頁面上時,mounted 會自動觸發。Vue 實例資料:
const rootComponent = {
data() {
return {
message: "I am Vue!"
};
},
mounted() {
console.log("this", this);
// 正確取得 message 變數方式
console.log("this.message", this.message);
// 以下方式取不到 message 變數
console.log("this.data", this.data);
console.log("this.data.message", this.data.message);
}
};
....略
瀏覽器 console.log 顯示結果如下。
印出this
的結果:
印出this.message
的結果:
印出this.data
及this.data.message
的結果:
實際上,這裡的this
指向的是經過 Vue 處理後的Proxy 物件
。
很多人會誤以為應該使用this.data
來訪問組件中的變數,但事實上,如果這樣做,你會發現返回的是 undefined
。
這是因為 Vue 在內部對this
進行了特殊的處理。this
是 Vue 代理過的Proxy 物件
,它使我們可以直接透過this.xxx
來訪問 data 中的變數,而不需要使用this.data.xxx
。這種設計使得訪問組件狀態的操作更加直觀和方便,減少了不必要的中間層級。
在 Vue 中,響應式變數是指當數據變更時,能夠自動觸發模板的重新渲染,從而更新畫面上的顯示內容。而一般變數則沒有這種能力,當它的值變更時,不會自動觸發模板的更新。
那麼,如果將響應式變數賦值給一般變數,會發生什麼情況呢?
透過以下的案例並搭配三種情境快速了解一下:
👉 Vue Options API 響應式變數與原始值比較實作連結
const rootComponent = {
data() {
return {
message: [2024]
};
},
mounted() {
// 問題 1 ----
let newMessage = [2024];
console.log("Before - 響應式變數", this.message);
console.log("Before - 一般變數", newMessage);
console.log("Before 響應式變數與原始值比較", this.message === newMessage); // 結果:false
// 問題 2----
this.message = newMessage;
console.log("After - 響應式變數", this.message);
console.log("After - 一般變數", newMessage);
console.log("After 響應式變數與原始值比較", this.message === newMessage); // 結果:false
// 問題 3---
newMessage = [2025];
console.log("一般變數賦值後結果 - 響應式變數", this.message);
console.log("一般變數賦值後結果 - 一般變數", newMessage);
}
};
原因在於一個是 Vue 管理的 Proxy 物件,另一個是普通的 JavaScript 陣列物件。即使它們內容相同,因為它們指向不同的引用,結果還是不會相等。
當你將一個普通變數賦值給響應式變數時,Vue 的響應式系統會將這個普通變數包裹在一個新的 Proxy 代理中進行管理。因此,響應式變數和普通變數的引用不同,即使它們的內容相同,兩者的比較結果仍然是不相等的
因為一般變數本身不具備響應性,所以它的改變不會觸發 Vue 的響應式系統。只有透過this.message(即 Proxy 代理)
來操作,Vue 才能監控並響應這些變化。
⭐ data()使用函式的方式返回定義的變數。主要是因為Template
透過Getter
的方式取值這樣可以確保每個組件實例都有自己獨立的數據副本,彼此不會互相影響。在實際開發中,通常會有多個 Vue 組件實例同時使用,這種設計可以確保數據的獨立性和可靠性。
👉 Vue3 Options API 組件內數據各自獨立不被影響實作連結
以下圖為例,即使使用多個相同的子組件且每個子組件擁有相同的數據,這些數據也是彼此獨立的,不會因為其他子組件的變化而受到影響。
在預設情況下,Vue 的 Options API 能夠偵測到數據中的物件
和陣列
的變動,並根據這些變動即時更新和渲染視圖。
以下面案例數據來說,不論是改動usrinfo.name
、productList
或userinfo.like
都會被 Vue 捕獲到這個操作,並將改動的結果響應式的反應在視圖上。
👉 Vue3 Options API 數據深層響應性實作連結
const rootComponent = {
data() {
return {
// 用戶資訊
userInfo: {
name: "Antonio",
age: 16,
address: "Taiwan",
like: ["Vue", "React", "Angular"]
},
// 產品列表
productList: ["Apple", "Banana", "Orange"]
};
},
...略
}
get
和set
函數攔截對象的訪問和修改。這使得 Vue 可以實現深度監控數據的變化,並將變更即時更新到視圖中。this.xxx
直接訪問data
中的響應式變數
,而不需要使用this.data.xxx
的方式。Proxy
的存在而導致結果不同。這意味著即使兩個對象的內容相同,它們的引用不同,因此比較結果可能為false
。