รู้จักกับ computed properties
Document ต้นฉบับ: https://vuejs.org/guide/essentials/computed.html
Computed properties คืออะไร ?
computed properties คือการห่อ logic หรือการคำนวนของ data เอาไว้แล้ว return ค่าออกไปเป็น result ตัวใหม่
pain point ของ computed properties คือ เราอาจจะมีโจทย์ที่ต้องการใส่ Logic กับตัวแปรไว้ แต่ไม่ต้องการมาคอยใส่ logic ใน template เช่น
- เคสหาความยาวของ Array
- เคสเอา string มาต่อกัน เช่น ชื่อ, นามสกุล
เราก็เลยจะใช้ computed มาแก้ปัญหานี้
ตัวอย่าง computed และ ความแตกต่างกับ method
เช่น เคสนี้ เราสร้างตัวแปร ชื่อจริง (firstName) และ นามสกุล (lastName) และให้สามารถ update ผ่าน input ได้ แล้วเอามาต่อกัน แสดงผลใหม่เป็น fullName
- เราก็จะสร้าง computed ชื่อ fullName ขึ้นมาแล้วต่อ firstName, lastName เอาไว้
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('')
const lastName = ref('')
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
</script>
<template>
<div>
<div>Your fullname: {{ fullName }}</div>
<div>
Firstname <input type="text" v-model="firstName">
</div>
<div>
Lastname <input type="text" v-model="lastName">
</div>
</div>
</template>
ผลลัพธ์
ทีนี้พอดูผลลัพธ์หลายคนอาจจะสงสัย แล้วมันต่างกันใช้ method ยังไง ? เพราะก่อนหน้านี้เราก็สร้าง method กันมา method เองก็สามารถที่จะทำแบบนี้ได้ เช่นแบบนี้
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('')
const lastName = ref('')
const fullName = () => {
return `${firstName.value} ${lastName.value}`
}
</script>
<template>
<div>
<div>Your fullname: {{ fullName() }}</div>
<div>
Firstname <input type="text" v-model="firstName">
</div>
<div>
Lastname <input type="text" v-model="lastName">
</div>
</div>
</template>
ก็จะเจอว่าได้ผลลัพธ์ออกมาเหมือนกันเลย ความแตกต่างของ 2 วิธีนี้คือ
- computed properties จะสามารถ cached ข้อมูลตามข้อมูล reactive ที่เข้ามาได้
- หมายความว่า
computed()
จะเกิดการเรียกไปยังตัวแปรหลัก (re-evaluate) แค่ตอนที่ตัวแปรที่computed()
เรียกใช้อยู่ มีการเปลี่ยนแปลงเท่านั้น (ในทีนี้ก็คือ firstName กับ lastName ซึ่ง computed() จะโดน update เมื่อ 2 ตัวแปรนี้มีตัวใดตัวหนึ่งเปลี่ยนค่า) - ซึ่งถ้าเป็นเคส method ถ้า template มีการเปลี่ยนแปลง = ก็จะเป็นการเรียกไปยังตัวแปรต้นทางใหม่เสมอ
- ดังนั้น
computed()
จึงถูกสร้างขึ้นมาให้ใช้กับตัวแปร reactive โดยเฉพาะ ไม่เหมาะกับใช้เคสที่ตัวแปรไม่ใช่ reactive เช่น
const now = computed(() => Date.now())
จะเจอว่าเวลาจะไม่เปลี่ยนไป ไม่ว่า template จะเปลี่ยนไปเท่าใดก็ตาม
ข้อดีของการ cached ของ computed คือการที่มันจะไม่ต้องคำนวนซ้ำอยู่ตลอดเวลา
- เช่นถ้าเป็นเคสที่เราทำกับ loop เช่นอาจจะเอาของใน list มาจัดการ ตราบเท่าที่ตัวแปรไม่มีการเปลี่ยนแปลง
computed()
ก็จะไม่เกิดการคำนวนใหม่ได้
การเขียนผ่าน computed
- โดย default ของ
computed()
หากประกาศเมื่อด้านบน = เป็นการเรียกใช้ตัวแปรแบบgetter
ซึ่งจะสามารถทำได้เพียงแค่แสดงผลออกมาเท่านั้น - ทีนี้แล้วถ้าเกิดเราจะประยุกต์ใช้กับเคสที่เราต้องการ update computed ตัวแปรละ = เราต้องใช้
setter
ของ computed มาช่วย
ขอตัวอย่าง อีกสักเคสเพื่อให้สมจริงยิ่งขึ้น สมมุติเราจะทำเว็บสำหรับ convert เงินไปมาระหว่าง USD กับ THB โดยที่เราไม่ต้องการสร้างตัวแปรแล้วค่อยมาคำนวนแยกกัน แต่เราอยากให้มันคำนวนไปมาหากันได้โดยที่เปลี่ยนได้ทั้ง USD และ THB = เราสามารถใช้ setter
ของ computed ช่วยได้
<script setup>
import { ref, computed } from 'vue'
const priceUSD = ref(0)
const exchangeRate = 35
const priceTHB = computed({
get: () => priceUSD.value * exchangeRate,
set: (newValue) => {
priceUSD.value = (newValue / exchangeRate)
}
})
</script>
<template>
<div>
Price in USD <input type="number" v-model="priceUSD">
Price in THB <input type="number" v-model="priceTHB">
<p>Price in USD: ${{ priceUSD }}</p>
<p>Price in THB: {{ priceTHB }}฿</p>
</div>
</template>
ผลลัพธ์
ตามประสบการณ์ส่วนตัว
computed()
ผมมักจะใช้กับเคสที่เกี่ยวกับการแสดงผล เสริมก ารแสดงผล (คล้ายๆเคส fullname)- จะไม่ค่อยแนะนำให้ใช้เคส
setter
เพราะจะทำให้สับสนได้ว่า data ถูกแก้จากใครไปได้ (มาจาก v-model ใน input หรือมาจาก setter ใน computed() กันแน่) ** ถ้าใครมีเคสดีๆลองแนะนำกันได้ แต่ส่วนตัวจะไม่ใช้ - จะไม่ใช้
computed()
กับ async, await หรือการแปลง DOM ในนี้เพราะจะทำให้ code ชวนสับสนขึ้นว่าcomputed()
จริงๆมีหน้าที่อะไรกันแน่ (ซึ่งเรื่องนี้แนะนำให้ใช้ method หรือจะใช้ watcher ที่เราจะพูดถึงกันต่อได้)