การ import Component และการส่ง properties
เล่าพื้นฐานของระบบ Component เพิ่มเติมก่อน
https://vuejs.org/guide/essentials/component-basics.html
Component คือการแบ่งส่วน UI แยกออกจากกันและสามารถนำกลับมา reuse ใหม่ได้
ภาพจาก document Vue
ซึ่ง component แต่ละอันเราจะใช้ .vue
เป็นการกำหนดเอาไว้ เราจะเรียกไฟล์นี้ว่า Vue Single - File Components
https://vuejs.org/guide/scaling-up/sfc.html
เริ่มต้นจากการลอง import component อื่นเข้ามากัน
├── src
│ ├── App.vue
│ ├── components
│ │ └── Counter.vue <-- เพิ่มไฟล์นี้เข้ามาใน src
│ ├── main.js
Counter.vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
App.vue
<script setup>
import Counter from './components/Counter.vue'
</script>
<template>
<h1>Here is a child component!</h1>
<Counter />
</template>
ก็จะสามารถ import component เข้ามาได้ และได้ผลลัพธ์ตามนี้
รู้จักกับ Props
Document ต้นฉบับ: https://vuejs.org/guide/components/props.html
การส่ง Props ใน Component คือการส่งค่าบางอย่างจากอีก Component หนึ่งไปสู่อีก Component หนึ่ง
โดยสิ่งที่เราจะต้องทำตอนส่ง Props คือ
- ตัว Component ที่เป็นตัวรับ ต้องกำหนดว่าจะรับ Props อะไรเข้ามาบ้าง
- ตัว Component ที่เป็นตัวส่ง ต้องส่งผ่าน attribute ที่กำหนดในตัวรับไว้
เรามาลองดูตัวอย่างเคสนี้กัน เราจะลองส่ง message บางอย่าง จาก App.vue ไปยัง Counter.vue กัน
Counter.vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
defineProps({
message: String
})
</script>
<template>
<h1>{{ message }}</h1>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
App.vue
<script setup>
import Counter from './components/Counter.vue'
import { ref } from 'vue'
const currentMessage = ref('Hello')
</script>
<template>
<h1>Here is a child component!</h1>
<Counter :message='currentMessage' />
</template>
ผลลัพธ์
จาก code ชุดนี้คือ
- ที่
Counter.vue
เรากำหนดการรับ props มาชื่อ message โดยทำการระบุ type ไว้ว่าจะรับเป็น string เข้ามา - ที่
App.vue
ทำการส่ง props ผ่าน attribute ที่ชื่อ message ชื่อเดียวกัน (:message
) เข้าไปใส่Counter.vue
โดยส่งผ่านตัวแปรที่ชื่อcurrentMessage
ส่งเข้าไป - ดังนั้นที่
Counter.vue
ก็จะสามารถแสดงผล message ออกมาได้
คำถามก็คือ แล้วถ้าเกิดเรามีการเปลี่ยนแปลง currentMessage ละ สมมุติเราเอาไปผูกกับ v-model ข้อมูล message จะเปลี่ยนตามด้วยไหม
คำตอบคือ ใช่ครับ เนื่องจาก currentMessage ที่ส่งไปเป็นตัวแปรแบบ reactive อยู่แล้ว
เราจะลองเพิ่มใน App.vue เข้าไปดู
<script setup>
import Counter from './components/Counter.vue'
import { ref } from 'vue'
const currentMessage = ref('Hello')
</script>
<template>
<h1>Here is a child component!</h1>
<input v-model="currentMessage">
<Counter :message='currentMessage' />
</template>
ผลลัพธ์
อันนี้คือคุณสมบัติของ Dynamic Props คือการที่ Props สามารถเปลี่ยนแปลงค่าตาม reactive ของตัวที่ส่งไปได้
ซึ่งจริงๆ props สามารถส่งได้ทั้ง Static Props (ส่งไปค่าคงที่) และเป็น Dynamic Props (ส่งเป็นตัวแปร reactive)
เช่น แบบนี้
<!-- ส่งเป็นค่าคงที่ได้ -->
<Counter message="Hello message" />
<!-- หรือจะส่งเป็นผสมกันก็ได้ -->
<Counter :message="'Hello message' + currentMessage" />
รวมถึงสามารถส่งเป็นประเภทไหนก็ได้ตั้งแต่ Number, Boolean, Array, Object
<!-- ส่งเป็นประเภท Number -->
<Counter :likes="42" />
<!-- ส่งเป็นประเภท Boolean -->
<Counter :is-published="false" />
<!-- ส่งเป็นประเภท Array -->
<Counter :comment-ids="[234, 266, 273]" />
<!-- ส่งเป็นประเภท Object -->
<Counter
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
แต่จะต้อง definedProps ให้ถูกประเภทด้วยเช่นกัน
defineProps({
message: String,
isPublished: Boolean,
commentIds: Array,
author: Object,
// สามารถกำหนด default ได้
propC: {
type: Number,
default: 100
},
propE: {
type: Object,
default(rawProps) {
return { message: 'hello' }
}
},
// สามารถกำหนดได้ว่า require หรือไม่
propC: {
type: String,
required: true
},
// รวมถึงสามารถส่ง Props เป็น function ก็ได้
propG: {
type: Function
}
})
ยกตัวอย่างการส่ง Props ด้วย functions
ขอเล่าข้อจำกัดอย่างหนึ่งก่อนนะครับ
- Props ไม่สามารถทำ data-binding
- เช่น อย่างเคสนี้ หากเราต้องการเปลี่ยน
message
ที่อยู่ใน App.vue จะไม่สามารถทำผ่านCounter.vue
ตรงๆได้
ดังนั้น อย่างที่เห็นด้านบน นอกเหนือจากการส่งตัวแปร มันสามารถส่ง function เข้าไปได้ด้วยเช่นกัน เราจะมาลองส่ง function สำหรับการ modified message เข้าไปด้วยกัน
เช่นจากเคสด้านบน หากเราส่ง function เปลี่ยน message (ชื่อ changeMessage) ไปใน Counter.vue
ที่สามารถเปลี่ยน message ได้ ก็จะสามารถทำเคสนี้ได้
ไฟล์ก็จะออกมาหน้าตาประมาณนี้
App.vue
<script setup>
import Counter from './components/Counter.vue'
import { ref } from 'vue'
const currentMessage = ref('Hello')
// สร้าง function changeMessage เข้ามา
const changeMessage = (newMessage) => {
currentMessage.value = newMessage
}
</script>
<template>
<h1>Here is a child component!</h1>
<Counter
:message='currentMessage'
:changeMessage='changeMessage'
/>
</template>
Counter.vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const newMessage = ref('')
defineProps({
message: String,
changeMessage: Function
})
</script>
<template>
<h1>{{ message }}</h1>
<input type="text" v-model="newMessage">
<button @click="changeMessage(newMessage)">Change Message</button>
<div>
<button @click="count++">You clicked me {{ count }} times.</button>
</div>
</template>
ประมาณนี้ เล่าจากประสบการณ์ของตัวผมเอง
- ปกติผมจะใช้ Props กับเคสที่เกี่ยวกับการแสดงผล เช่น เราสร้าง Form component หรือ Popup Component ขึ้นมาแล้วส่งค่าผ่าน Props เพื่อทำการแสดงผล หรือส่งค่าบางอย่างเพื่อให้ใช้ function ผ่าน Popup Component ได้
- ส่วนเคสส่ง function จะเป็นการใส่ custom function ในจุดที่มีการ mark ไว้แล้ว เช่น ถ้ากดปุ่มปิด จะให้ทำอะไรต่อ เป็นต้น
เรื่องนี้เราจะปูพื้นโดยประมาณก่อน เพราะเดี๋ยวเราจะมีโอกาสได้ใช้แบบจัดหนักจัดเต็มเร็วๆนี้แน่นอน