Skip to main content

การ import Component และการส่ง properties

เล่าพื้นฐานของระบบ Component เพิ่มเติมก่อน

https://vuejs.org/guide/essentials/component-basics.html

Component คือการแบ่งส่วน UI แยกออกจากกันและสามารถนำกลับมา reuse ใหม่ได้

vue-multi-component

ภาพจาก 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-01

รู้จักกับ Props

Document ต้นฉบับ: https://vuejs.org/guide/components/props.html

การส่ง Props ใน Component คือการส่งค่าบางอย่างจากอีก Component หนึ่งไปสู่อีก Component หนึ่ง

โดยสิ่งที่เราจะต้องทำตอนส่ง Props คือ

  1. ตัว Component ที่เป็นตัวรับ ต้องกำหนดว่าจะรับ Props อะไรเข้ามาบ้าง
  2. ตัว 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>

ผลลัพธ์ props-02

จาก 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>

ผลลัพธ์ props-03

อันนี้คือคุณสมบัติของ 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-04

ประมาณนี้ เล่าจากประสบการณ์ของตัวผมเอง

  • ปกติผมจะใช้ Props กับเคสที่เกี่ยวกับการแสดงผล เช่น เราสร้าง Form component หรือ Popup Component ขึ้นมาแล้วส่งค่าผ่าน Props เพื่อทำการแสดงผล หรือส่งค่าบางอย่างเพื่อให้ใช้ function ผ่าน Popup Component ได้
  • ส่วนเคสส่ง function จะเป็นการใส่ custom function ในจุดที่มีการ mark ไว้แล้ว เช่น ถ้ากดปุ่มปิด จะให้ทำอะไรต่อ เป็นต้น

เรื่องนี้เราจะปูพื้นโดยประมาณก่อน เพราะเดี๋ยวเราจะมีโอกาสได้ใช้แบบจัดหนักจัดเต็มเร็วๆนี้แน่นอน