在 Day 6 在使用 Vue 的Mustache
語法時,可以用來放置表達式。不過,這邊建議避免在 Mustache 中撰寫過於複雜的邏輯。這樣的建議主要有兩個原因:
下面的例子是使用 Vue Mustache
放置複雜邏輯的一個典型情境。表面上看起來簡單,實際上依賴關係不明確且難以維護。更關鍵的是,這段表達式會在每次模板重新渲染時重新執行,導致潛在的性能問題。
👉 Vue3 Options API Mastache 複雜邏輯實作連結
HTML:
<div id="app">
<div class="input-group">
<label for="isShow">是否公開</label>
<input id="isShow" type="checkbox" v-model="productInfo.isShow">
</div>
<div>
<h2>產品清單</h2>
<div class="content">
{{ productInfo.productList.length > 0 && productInfo.isShow? productInfo.productList : '產品尚未公開' }}
</div>
</div>
</div>
延續上面的案例,一個常見的做法是將複雜的邏輯從模板中移至methods
,以保持模板的清潔。
這樣確實讓模板看起來更簡潔,但仍然存在一個關鍵問題:模板每次更新時,仍會重複執行showProductMsg 函數
。
👉 Vue3 Options API Mastache 複雜邏輯(method 優化)實作連結
HTML:
<div>
<h2>產品清單</h2>
<div class="content">
{{ showProductMsg() }}
</div>
</div>
javaScript:
methods: {
showProductMsg() {
return this.productInfo.productList.length > 0 && this.productInfo.isShow
? this.productInfo.productList
: "產品尚未公開";
}
}
...略
讓我們加上一個定時器來觀察這個問題。
👉 Vue3 Options API methods 複雜邏輯實作連結
HTML:
<div id="app">
當前時間:{{ time }}
<div class="input-group">
<label for="isShow">是否使用</label>
<input id="isShow" type="checkbox" v-model="productInfo.isShow">
</div>
<div>
<h2>產品清單</h2>
<div class="content">
{{ showProductMsg() }}
</div>
</div>
</div>
javaScript:
const rootComponent = {
data() {
return {
time: Date.now(),
productInfo: {
isShow: false,
productList: ["Apple", "Banana", "Orange"]
}
};
},
methods: {
showProductMsg() {
console.log("showProductMsg 被觸發");
return this.productInfo.productList.length > 0 && this.productInfo.isShow
? this.productInfo.productList
: "產品尚未公開";
}
},
// 設定定時器(300ms 更新一次 time)
mounted() {
setInterval(() => (this.time = Date.now()), 300);
}
};
如下圖所示,模板因為定時器不停變更time
數據,每次time
改變時,模板重新渲染,這導致每次渲染都會執行一次showProductMsg 函數
。即使showProductMsg
方法依賴的數據productInfo.isShow
和 productInfo.productList
並未發生變化,它仍會在每次渲染時重新執行。
為了解決這個問題,可以使用 Vue 計算屬性(computed),其取值方式透過getter
的方式,且具有緩存資料
的特性,當依賴項沒有改動的時候不會重新觸發。
⭐ 計算屬性會在初始化時觸發一次,並執行其getter 函數
來計算初始結果。
將原本methods
的函式,改成計算屬性在看一次可以發現,計算函式在time
變動的時候不會觸發,唯有在點擊複選框的時候,因為其依賴值productInfo.isShow
改變,才會觸發計算屬性的getter
。
基本上computed
與methods
方法執行並回傳一個表達式結果相同,但特別注意這邊是屬性,因此在模板使用的時候,不再是showProductMsg()
而是使用showProductMsg
取讀取計算屬性。
👉 Vue3 Options API methods 複雜邏輯(computed 優化)實作連結
javaScript:
computed: {
showProductMsg() {
console.log("showProductMsg 被觸發");
return this.productInfo.productList.length > 0 && this.productInfo.isShow
? this.productInfo.productList
: "產品尚未公開";
}
}
...略
計算屬性默認是只可以讀,如果要寫入必須同時提供getter
及setter
函式才可以,可透過setter
改動依賴值,並觸發getter
重新計算值。
在使用setter 函數
時,需特別注意不要更改計算屬性依賴項以外的數據,否則可能會導致數據流混亂,從而影響應用的響應式系統。
透過華氏跟攝氏轉換器案例帶大家了解,這個比較特別的用法:
👉 Vue3 Options API 華氏及攝氏轉換實作連結
HTML:
<div id="app">
<div class="card">
<h2>我是華氏輸入框</h2>
<input type="text" v-model="C">
</div>
<div class="card">
<h2>我是攝氏輸入框</h2>
<input type="text" v-model="F">
</div>
</div>
javaScript:
const rootComponent = {
data() {
return {
F: 0,
};
},
computed: {
C: {
// 取值的時候,根據攝氏轉換度數
get() {
console.log('get 觸發');
return (this.F * 9) / 5 + 32;
},
// 當我針對計算屬性寫入的時候改變原始值並觸發getter
set(value) {
console.log('set 觸發', value);
this.F = (value - 32) * 5 / 9;
}
}
}
}
v-model 可以理解為,當你改變輸入框中的數字時,對應的數據會自動更新,並且畫面也會同步顯示這個變更的數字。這實現了數據和界面之間的雙向綁定。在後續章節中,會詳細介紹這個雙向綁定的概念。
如下圖所示,這邊華氏輸入框
綁定C(華氏)計算屬性,綁定依賴項是當F(攝氏)。
當你在攝氏輸入框
中輸入數字時,會改變攝氏的值,並觸發計算屬性的get() 函數
,從而重新計算並顯示當前轉換後的華氏數值。
相反地,當你在華氏輸入框
中輸入數字時,會觸發set() 函數
,這會更新攝氏的值,並因此觸發get() 函數
,重新計算華氏的數值,實現雙向同步。
👉 Vue Options API 違反 Getter 副作用案例實作連結
👉 Vue3 Options API 違反 computed 修改回傳值案例實作連結
當資料量較少時,通常會選擇將資料一次性回傳至前端,並透過前端程式進行檢索或分頁。此時,計算屬性可以作為實現關鍵字檢索功能的方案。
👉 Vue3 Options API computed filter 實作連結
HTML:
<div id="app">
<div>
搜尋框: <input type="text" v-model="filter">
</div>
<ul>
<li v-for="item of getFilterProduct" :key="item.id">{{ `${item.name} / ${item.price} ` }}</li>
</ul>
</div>
JavaScript:
const rootComponent = {
data() {
return {
filter: "",
productList: [
{
id: 1,
name: "漢堡",
price: 30
},
{
id: 2,
name: "甜甜圈",
price: 45
},
{
id: 3,
name: "熱狗",
price: 25
}
],
sum: 0
};
},
computed: {
// 取得檢索項目(若空白則全部回傳)
getFilterProduct() {
const filterResult = this.productList.filter((product) => {
return product.name.match(this.filter);
});
return filterResult;
}
}
}
getter 函數
應保持純粹,不應產生副作用,例如:修改非依賴的數據或發送異步請求,以確保應用的響應式數據流正常運作。getter
和setter
函數。setter
應僅修改其依賴的原始數據,避免引入額外的副作用,否則可能會導致數據流混亂。這邊讓大家練習看看幣值匯率轉換,讓大家也能試試看計算屬性的setter
觸發方式。
可以按照今天學到的計算屬性試著完成一個幣值轉換器,透過計算屬性可寫的setter
達成目的。
(僅需調整計算屬性 setter 的部分即可)
👉 Vue Options API 匯率轉換器(computed set 應用)未完成版本連結
👉 Vue Options API 匯率轉換器(computed set 應用)完成版本連結