เนื้อหานี้ต่อจากตอนแรกเกี่ยวกับ component เบื้องต้น จะมาดูเกี่ยวกับ
การกำหนด property หรือ props ซึ่งในขั้นตอนการเรียกใช้งาน เรา
ต้องการให้คอมโพเนนท์รองรับค่าจากการส่งเข้ามาเพื่อใช้งาน เพื่อให้เห็นภาพ
จะยกตัวอย่างโค้ดจากตอนที่แล้วเข้ามาประกอบการอธิบาย
การกำหนด Props ให้กับ Component
ใน ButtonCounter คอมโพเนนท์ในตอนที่แล้ว เราทำการนับจำนวนการคลิก
ซึ่งนับจาก 0 และเพิ่มทีละ 1 ค่า แต่สมมติว่า เราต้องการให้คอมโพเนนท์นี้ รองรับให้
สามารถกำหนดค่าเริ่มต้น และกำหนด step ของการเพิ่มค่าได้ เช่น แทนที่จะเป็น
บวกเพิ่มทีละ 1 ค่า ก็สามารถบวกตามที่ค่าที่กำหนดเข้ามาได้ เราสามารถทำได้ดังนี้
รูปแบบเดิม ButtonCounter.vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
You clicked me {{ count }} times.
</button>
</template>
ปรับเป็นดังนี้ ButtonCounter.vue
<script setup>
import { ref } from 'vue'
const props = defineProps({
start: {
type: Number,
required: true,
default: 0,
validator: value => value >= 0
},
step:{
type: Number,
default: 1,
validator: value => value >= 1
},
})
const count = ref(props.start)
function increment(){
count.value +=props.step
}
</script>
<template>
<button @click="increment">
You clicked me {{ count }} times.
</button>
</template>
defineProps เป็นคำสั่ง macro ใน vuejs ที่มีใช้ในส่วนของ <script setup> เพื่อทำให้การ
เขียนโค้ดง่ายขึ้นและกระชับขึ้น โดยการลดส่วนโค้ดที่ซ้ำซ้อน macro ที่เด่นชัดใน VueJS ได้แก่
defineProps, defineEmits, และ defineExpose.
การใช้งาน defineProps กำหนดได้ทั้งแบบ array หรือ object
// using Array syntax
const props = defineProps(['foo', 'bar'])
// using Object syntax
const props = defineProps({
foo: String,
bar: {
type: Number,
required: true
}
})
หาก props ที่เรากำหนดเป็นข้อมูลแบบ String หรือข้อความ เราสามารถกำหนดในรูปแบบ
array ได้จะสะดวกกว่า แต่ถ้าเป็นตัวเลข หรือแบบที่ซับซ้อนกว่า หรือต้องการกำหนดเงื่อนไขเพิ่ม
เติม เช่น กำหนดชนิดข้อมูล มีค่าเริ่มต้น หรือกำหนดว่าต้อง ใช้งานหรือมี props นี้ทุกครั้งที่
เรียกใช้ หรือกำหนดการตรวจสอบข้อมูลว่าถูกต้องหรือไม่ เช่น เป็นค่ามากกว่าหรือเท่ากับ 0
แบบนี้เราจะใช้เป็นการกำหนดแบบ object ในตัวอย่าง เรากำหนดให้ทั้ง start และ step
เป็นตัวเลข และมีค่าเริ่มต้นเท่ากับ 0 และ 1 ตามลำดับ และเงื่อนไขการตรวจสอบ ต้องมากกว่า
หรือเท่ากับ 0 สำหรับ start และต้องมากกว่าหรือเท่า 1 สำหรับ step
เมื่อเรากำหนดเสร็จแล้ว ในขั้นตอนการเรียกใช้งาน เราก็จะปรับไฟล์โค้ดใน App.vue เป็นดังนี้
App.vue
<script setup> import ButtonCounter from './components/ButtonCounter.vue' </script> <template> <h1>Here are many child components!</h1> <ButtonCounter :start="2" :step="3" /> <ButtonCounter :start="0" /> </template>
จะเห็นว่าเมื่อเรากำหนดว่า start ต้องมีเสมอ ทำให้ตัวที่สอง ถึงค่า start จะเป็น 0 แต่เราก็จำเป็น
จะต้องเพิ่ม props เข้าไป ถึงแม้ว่าเราไม่เพิ่ม แต่โค้ดก็ยังทำงานปกติ จะมีแค่แจ้งเตือนใน console
ว่า [Vue warn]: Missing required prop: "start" ทั้งนี้ก็เพราะเรากำหนดค่าเริ่มต้นไว้ทั้ง
start และ step และไม่ได้กำหนดการตรวจสอบการจัดการไว้ สมมติว่าถ้าเรากำหนดการตรวจสอบ
การจัดการไว้เป็นดังนี้
ButtonCounter.vue
ButtonCounter.vue
<script setup>
import { ref } from 'vue'
const props = defineProps({
start: {
type: Number,
required: true,
validator: value => value >= 0
},
step:{
type: Number,
default: 1,
validator: value => value >= 1
},
})
// ตรวจสอบว่า prop 'start' ถูกส่งมาหรือไม่
if (props.start === undefined) {
throw new Error('Missing required prop: "start"');
}
const count = ref(props.start)
function increment(){
count.value +=props.step
}
</script>
<template>
<button @click="increment">
You clicked me {{ count }} times.
</button>
</template>
เรากำหนดให้ start จำเป็นต้องมี และไม่กำหนดค่าเริ่มต้น และให้มีการตรวจสอบว่ามีการส่งค่า
มาหรือไม่ ถ้าไม่มีการส่งค่ามาก็จะให้แจ้ง error ออกไป สมมติเราใช้งานเป็นดังนี้
App.vue
<script setup> import ButtonCounter from './components/ButtonCounter.vue' </script> <template> <h1>Here are many child components!</h1> <ButtonCounter :start="2" :step="3" /> <ButtonCounter /> </template>
ผลลัพธ์ที่ได้

มี error เกิดขึ้นและ คอมโพเนนท์ตัวที่สองไม่ถูกนำมาแสดง เพราะไม่ได้กำหนด props ชื่อ
start เข้าไปตามรูปแบบที่กำหนดไว้
จะสังเกตเห็นว่า required และ validator ใน defineProps จะเป็นการกำหนดให้เราเห็น
ชัดว่ารูปแบบที่ต้องการเป็นอย่างเท่านั้น แต่ในเรื่องของการตรวจสอบ เราจำเป็นต้องสร้างเงื่อนไข
เพิ่มเติมเข้าไป หากต้องการตรวจสอบที่มากขึ้น เช่น
// ตรวจสอบว่า prop 'start' ถูกส่งมาหรือไม่และผ่านการ validate หรือไม่
if (props.start === undefined || !props.start >= 0) {
throw new Error('Invalid prop: "start" must be a number greater than or equal to 0');
}
ดังนั้นเมื่อมีการนำไปใช้งานการกำหนดในรูปแบบ object เราต้องตรวจสอบให้ถูกต้อง
อีกข้อสังเกตหนึ่งคือ โดยทั่วไป เวลากำหนด props ต่างๆ หรือ attribute ต่างๆ ใน html จะเป็น
การส่งข้อมูลเป็น string เข้าไป ซึ่งถ้าเราใช้รูปแบบเป็น
<ButtonCounter start="2" step="3" />
จะกลายเป็นว่า เลข 2 และ 3 จะเป็นข้อมูล String ซึ่งไม่ตรงตามที่เรากำหนด ทำให้การทำงาน
อาจจะผิดพลาดได้ ด้วยเหตุนี้ กรณีที่ props ของเราไม่ใช่ข้อความหรือ String เราจึงใช้วิธีการ
เรียกใช้งาน v-bind directive ในรูปแบบ v-bind:start="" หรือ เขียนแบบย่อๆ เป็น :start=""
ซึ่งจะทำให้ vue ทำการตรวจสอบและแปลงข้อมูลเลข 2 กับ 3 ไปเป็นตัวเลข แทนที่จะเป็นข้อความ
เพื่อให้ตรงกับรูปแบบ props ที่เรากำหนดในคอมโพเนนท์ เราจึงนิยมใช้แบบนี้แทน
<ButtonCounter :start="2" :step="3" />
ดูเพิ่มอีกตัวอย่าง
ให้เราสร้างคอมโพเนนท์ชื่อ BlogPost.vue ใน src > components ด้วยรูปแบบอย่างง่าย
และกำหนด props เข้าไปดังนี้
BlogPost.vue
<script setup>
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>
และเรียกใช้งานในไฟล์ App.vue ดังนี้
App.vue
App.vue
<script setup> import ButtonCounter from './components/ButtonCounter.vue' import BlogPost from './components/BlogPost.vue' </script> <template> <h1>Here are many child components!</h1> <ButtonCounter :start="2" :step="3" /> <ButtonCounter /> <BlogPost title="My journey with Vue" /> <BlogPost title="Blogging with Vue" /> <BlogPost title="Why Vue is so fun" /> </template>
ผลลัพธ์ที่ได้

สมมติเรามีข้อมูล และต้องการวนลูปแสดงด้วย v-for โดยใช้งานร่วมกับ BlogPost คอมโพเนนท์
ก็สามารถใช้งานได้ดังนี้
App.vue
App.vue
<script setup>
import { ref } from 'vue'
import ButtonCounter from './components/ButtonCounter.vue'
import BlogPost from './components/BlogPost.vue'
const posts = ref([
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
])
</script>
<template>
<h1>Here are many child components!</h1>
<ButtonCounter :start="2" :step="3" />
<ButtonCounter />
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
/>
</template>
ผลลัพธ์ที่ได้ก็จะเหมือนกับรูปตัวอย่างก่อนหน้าด้านบน
การกำหนด Event ให้กับ Component
ใน ButtonCounter เราเรียกใช้งาน onclick event เพื่อทำการเพิ่มจำนวนการคลิก แต่
เมื่อนำคอมโพเนนท์มาใช้ใน parent เราต้องการให้คอมโพเนนท์นั้นๆ มี event ของตัวเอง
เพื่อให้สามารถนำมาใช้งาน หรือสามารถเรียกใช้ event ของ คอมโพเนนท์ได้ เราต้องทำการ
กำหนด event ให้กับคอมโพเนนท์นั้น โดยสามารถทำได้ดังนี้
ButtonCounter.vue
ButtonCounter.vue
<script setup>
import { ref } from 'vue'
const props = defineProps({
start: {
type: Number,
default: 0
},
step:{
type: Number,
default: 1
},
})
// กำหนด event ที่จะปล่อยไปกับคอมโพเนนท์
const emit = defineEmits(['increment','reset'])
const count = ref(props.start)
function increment(){
count.value +=props.step
// ปล่อย increment โดยส่งค่าข้อมูลออกไปด้วย
emit('increment', count.value)
}
function resetstart(){
count.value = props.start
// ปล่อย reset โดยไม่ส่งค่าข้อมูลออกไป
emit('reset')
}
</script>
<template>
<button @click="increment" @dblclick="resetstart" >
You clicked me {{ count }} times.
</button>
</template>
เริ่มต้นที่เรากำหนดชื่อ event ที่ต้องการ
// กำหนด event ที่จะปล่อยไปกับคอมโพเนนท์ const emit = defineEmits(['increment','reset'])
จากนั้นส่งออกค่าในคำสั่ง หรือเหตุการณ์ ที่ต้องการ
function increment(){
// ปล่อย increment โดยส่งค่าข้อมูลออกไปด้วย
emit('increment', count.value)
}
function resetstart(){
// ปล่อย reset โดยไม่ส่งค่าข้อมูลออกไป
emit('reset')
}
สังเกตการทำงานใน template ของคอมโพเนนท์
<button @click="increment" @dblclick="resetstart" >
You clicked me {{ count }} times.
</button>
เรากำหนดว่า เมื่อคลิก ให้ทำคำสั่ง increment() คือเพิ่มจำนวน และเมื่อเพิ่มจำนวนแล้ว
ก็ให้ปล่อย event ที่ชื่อว่า increment ออกไป พร้อมกับค่าข้อมูล count ณ ปัจจุบัน
ในขณะที่ เมื่อกดดับเบิลคลิก ให้ทำคำสั่ง resetstart() คือให้ค่าเริ่มต้นของ count กลับ
มาเป็นค่าเริ่มต้น ตามที่กำหนดตอนแรก และเมื่อรีเซ็ตค่าแล้ว ก็ให้ปล่อย event ที่ชื่อว่า
reset ออกไป โดยไม่มีการส่งค่าข้อมูลไปด้วย
สรุปการทำงานก็คือ คลิก 1 ครั้งเป็นการเพิ่มจำนวน กดดับเบิลคลิกหรือสองครั้งติดกัน จะเป็น
การรีเซ็ตค่าการนับใหม่เป็นค่าเริ่มต้น
เมื่อกำหนด event ให้กับคอมโพเนนท์แล้ว เราก็สามารถเรียกใช้งานในส่วนของ parent
ได้เป็นดังนี้
App.vue
App.vue
<script setup>
import { ref } from 'vue'
import ButtonCounter from './components/ButtonCounter.vue'
import BlogPost from './components/BlogPost.vue'
const posts = ref([
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
])
function countupdate(newValue){
console.log('Increment '+ newValue)
}
function countreset(){
console.log('Reset ')
}
</script>
<template>
<h1>Here are many child components!</h1>
<ButtonCounter :start="2" :step="3" />
<ButtonCounter @reset="countreset"
@increment="countupdate" />
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
/>
</template>
ดูในส่วนของการเรียกใช้งานที่ปุ่ม ButtonCounter
<ButtonCounter @reset="countreset" @increment="countupdate" />
จะเห็นรูปแบบการใช้งาน event โดยใช้ v-on:increment กับ v-on:reset ซึ่งเขียนแบบ
ย่อได้เป็น :increment="" กับ :reset="" ในตัวอย่าง เมื่อเกิด event ขึ้น เรากำหนดให้
ไปทำคำสั่ง countupdate และ countreset โดย countupdate จะรองรับข้อมูลที่ส่ง
มาพร้อมกับ event ด้วย
function countupdate(newValue){
console.log('Increment '+ newValue)
}
function countreset(){
console.log('Reset ')
}
จะเห็นว่ารูปแบบการใช้งาน event ที่เราสร้างขึ้น ก็จะสามารถใช้งานได้คล้ายกับ DOM element
ปกติที่เราคุ้ยเคย
เนื้อหาเกี่ยวกับการกำหนด props และ event รวมถึงการใช้งาน props และ event ที่กำหนด
ใน component เมื่อนำมาใช้งานใน parent ก็จะขอจบประมาณนี้ เนื้อหาเกี่ยวกับคอมโพเนนท์
เบื้องต้นยังมีต่อ รอติดตามในตอนที่ 3