Skip to main content

รู้จักกับ 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>

ผลลัพธ์

computed-01

ทีนี้พอดูผลลัพธ์หลายคนอาจจะสงสัย แล้วมันต่างกันใช้ 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-01

ตามประสบการณ์ส่วนตัว

  • computed() ผมมักจะใช้กับเคสที่เกี่ยวกับการแสดงผล เสริมการแสดงผล (คล้ายๆเคส fullname)
  • จะไม่ค่อยแนะนำให้ใช้เคส setter เพราะจะทำให้สับสนได้ว่า data ถูกแก้จากใครไปได้ (มาจาก v-model ใน input หรือมาจาก setter ใน computed() กันแน่) ** ถ้าใครมีเคสดีๆลองแนะนำกันได้ แต่ส่วนตัวจะไม่ใช้
  • จะไม่ใช้ computed() กับ async, await หรือการแปลง DOM ในนี้เพราะจะทำให้ code ชวนสับสนขึ้นว่า computed() จริงๆมีหน้าที่อะไรกันแน่ (ซึ่งเรื่องนี้แนะนำให้ใช้ method หรือจะใช้ watcher ที่เราจะพูดถึงกันต่อได้)