竹白的 Vue 記事本 目錄
Computed 計算
在專案開發中,資料往往需要經過一些處理,Vue 提供了 computed
選項,用來減輕模板上的資料處理,方便重複利用及維護。
1. computed
以反轉字串為例:
1
2
3
|
<div id="app">
<p>{{ message.split('').reverse().join('') }}</p>
</div>
|
若是在 Mustache 中寫一些簡單運算還能馬上理解。但像上面寫一長串程式碼,我們可能無法立刻理解它的邏輯。
因此,我們可以使用計算屬性,將程式碼改寫成這樣:
1
2
3
|
<div id="app">
<p>{{ reversedMessage }}</p>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
|
const vm = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('');
},
},
});
|
這樣不但方便重複使用與維護,也能馬上明白這段程式碼的意思。
另外要注意,因為 Vue 會將 computed
整合到實體中,因此不要與 data
、methods
重複宣告。
2. 與 method 的差異
雖然使用 method
一樣可以達到同樣的效果:
1
2
3
|
<div id="app">
<p>{{ reversedMessage() }}</p>
</div>
|
1
2
3
4
5
|
methods: {
reversedMessage() {
return this.message.split('').reverse().join('');
},
}
|
但計算屬性會將結果緩存,只在相關響應式依賴發生改變時它們才會重新計算。另外計算屬性沒辦法傳入參數。
響應式依賴發生改變是什麼意思呢?就是只有在計算屬性中使用被加到響應式系統的資料發生改變,計算屬性才會重新計算。
舉例來說,Date.now()
並不是響應式依賴,所以並不會重新計算。:
1
2
3
4
5
|
computed: {
now: function () {
return Date.now()
}
}
|
還有一點要注意,計算屬性要有使用才會計算,並不是依賴發生改變就重新計算。舉例來說,我們沒有在畫面上使用到 c1
:
1
2
3
4
5
6
7
8
9
10
11
12
|
const vm = new Vue({
el: '#app',
data: {
message: 'Hello',
},
computed: {
c1() {
console.log('c1');
return this.message + 1
},
},
});
|
我們在 Console 中改變 message
的值,並不會印出 'c1'
,也就是說 c1
並沒有被呼叫:
1
2
|
// Console
vm.message = 'Hi!';
|
但是如果你有使用 Vue.js devtools,它會全部呼叫一次才能顯示值給你,因此就算沒到使用也會呼叫。
回到剛提到的 method
,我們從以下程式碼,就可以觀察到兩者的差異:
1
2
3
4
5
6
7
8
|
<div id="app">
<p>{{ rmComputed }}</p>
<p>{{ rmComputed }}</p>
<p>{{ rmComputed }}</p>
<p>{{ rmMethods() }}</p>
<p>{{ rmMethods() }}</p>
<p>{{ rmMethods() }}</p>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const vm = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
},
computed: {
rmComputed() {
console.log('computed');
return this.message.split('').reverse().join('');
},
},
methods: {
rmMethods() {
console.log('methods');
return this.message.split('').reverse().join('');
},
},
});
|
// Console
'computed'
'methods'
'methods'
'methods'
我們在模板中重複訪問,你可以發現 rmComputed
只執行一次,而 rmMethods
會執行三次。
因為,只要跟 rmComputed
有關的 message
不變,就不會重新計算,會從緩存取得結果值。
3. setter
計算屬性函式型式預設為 getter,也可以改寫成物件型式新增一個 setter,當計算屬性被回寫 setter 就會被呼叫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
const vm = new Vue({
computed: {
a: {
get() {
return 10;
},
set(newVal) {
console.log('123');
},
},
},
});
vm.a = 0; // '123' ( a 被改變,所以執行 console.log('123')
|
注意,只有使用 setter 時,才可以回寫計算屬性。否則開發環境版本的 Vue 會在 Console 跳出警告。
我們來看一個匯率轉換的應用:
1
2
3
4
5
6
7
8
9
|
<div id="app">
<p>假設 1 美元 = 29.8 台幣</p>
<p>美元
<input v-model.number="usd" type="number" />
</p>
<p>新台幣
<input v-model.number="twd" type="number" />
</p>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
const vm = new Vue({
el: '#app',
data: {
usd: 1,
},
computed: {
twd: {
get() {
return this.usd * 29.8;
},
set(newVal) {
this.usd = newVal / 29.8;
},
},
},
});
|
當 twd
變化,就會呼叫 setter 改變 usd
的值。
4. 總結
計算屬性的應用:
- 分離邏輯
- 緩存結果
- 雙向綁定(setter 應用)
Watch 監聽
Vue 提供了一種更通用的方式來觀察和響應 Vue 實體上的資料變動,那就是 watch
選項。
1. watch
監聽資料,當資料發生改變,我們就做某件事:
1
2
3
4
5
6
7
8
9
10
11
12
|
const vm = new Vue({
el: '#app',
data: {
message: '',
count: 0,
},
watch: {
message() {
this.count += 1;
},
},
});
|
1
2
3
4
|
<div id="app">
<input type="text" v-model="message" >
<p>{{ count }}</p>
</div>
|
當 message
發生變化,就計數。
2. 參數
當監聽的資料發生變化,這個監聽函式會傳入兩個參數,分別為 newVal
與 oldVal
,變化後的新值、變化前的舊值。
1
2
3
4
5
|
watch: {
message(newVal, oldVal) {
// do Something
},
}
|
監聽屬性除了函式,也可以寫成物件型式:
1
2
3
4
5
6
7
|
watch: {
message: {
handler(newVal, oldVal) {
// do Something
},
},
}
|
物件型式有兩個選項可以設定,分別為 immediate
與 deep
。
資料若沒變動,就不會觸發監聽函式,若希望資料初始值也會執行 watch 的動作,我們可以將 immediate
設為 true
。
直接看應用範例,UNIX timestamp 格式轉換:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
const vm = new Vue({
el: '#app',
data: {
date: new Date().toLocaleDateString(),
timestamp: null,
},
watch: {
date: {
immediate: true,
handler() {
this.timestamp = this.turnTimestamp(this.date);
},
},
},
methods: {
turnTimestamp(date) {
const timestamp = Math.floor(new Date(date) / 1000);
return timestamp;
},
},
});
|
1
2
3
4
5
|
<div id="app">
<input type="date" v-model="date" >
<p>Date: {{ date }}</p>
<p>UNIX timestamp: {{ timestamp }}</p>
</div>
|
如果沒使用 immediate: true
,就需要在 created
鉤子上初始化 timestamp
:
1
2
3
|
created() {
this.timestamp = this.turnTimestamp(this.date);
}
|
4. 深度監聽
如果 data
是物件,要監聽裡面的屬性變化,該怎麼做呢?
物件型式的另一個選項為 deep
深度監聽,設為 true
即可,只要其中一個屬性值發生變化,就會執行指定動作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
data:{
object: {
a: '',
b: '',
}
},
watch: {
object: {
deep: true,
handler() {
// do Something
},
},
}
|
5. 總結
任何可以使用 computed
的功能,都能使用 watch
完成,因此很容自造成濫用 watch
。
簡單的區分使用場景:
- 簡單且單純的資料操作並用於畫面上,盡量使用
computed
;
- 如果需觸發行為再使用
watch
。
非同步操作就只能使用 watch
。