เนื้อหานี้จะเป็นตอนสุดท้ายของ คอมโพเนนท์เบื้องต้น หลักๆ แล้ว
เนื้อหาที่กล่าวถึง ก็จะเป็นพื้นฐานที่สำคัญ และสามารถทำให้เข้าใจ
ส่วนต่างๆ ที่จะตามมาเพิ่มเติมได้ เพราะคอมโพเนนท์คือส่วนที่สำคัญ
ใน vuejs เนื้อหานี้เราจะมาดูเกี่ยวกับการใช้งาน slot และดูเกี่ยวกับ
การใช้งาน Dynamic Component
Slot คืออะไร
Slots ใน Vue.js เป็นกลไกที่ทำให้เราสามารถใส่เนื้อหาที่กำหนดเองเข้าไปใน component
ได้ โดยไม่ต้องแก้ไข component ทำให้ component มีความยืดหยุ่นมากขึ้น
และสามารถนำมาใช้ซ้ำได้ กล่าวคือเราใช้ <slot> ใน component เพื่อกำหนดพื้นที่สำหรับ
แสดงข้อมูลหรือเนื้อหา เพื่อให้เห็นรูปแบบและการใช้งาน slot จะยกตัวอย่างดังนี้
ไฟล์ FancyButton.vue ใน src > components
<script setup> import { ref } from 'vue' </script> <template> <button class="fancy-button"> <slot>Default Button Text</slot> </button> </template> <style scoped> .fancy-button { background-color: #42b983; border: none; color: white; padding: 10px 20px; font-size: 16px; cursor: pointer; border-radius: 5px; } .fancy-button:hover { background-color: #38a172; } </style>
พิจารณาในส่วนนี้
<button class="fancy-button"> <slot>Default Button Text</slot><!-- slot outlet --> </button>
สังเกตว่า ส่วนที่ใช้งาน <slot> จะเป็นส่วนจะถูกแทนที่ด้วยข้อความหรือข้อมูลใหม่ หรือเข้าใจ
อย่างง่ายคือบริเวณที่จะถูกแทนที่ด้วยข้อมูลอื่น ซึ่งเดิมค่าเริ่มต้นที่เรากำหนดใน component
คือ Default Button Text
เราไปดูต่อที่ไฟล์ App.vue เมื่อนำมาใช้งาน จะได้เป็นดังนี้
<script setup> import { ref } from 'vue' import FancyButton from './components/FancyButton.vue' </script> <template> <FancyButton> Click me! <!-- slot content --> </FancyButton> <br><br> <FancyButton> Save <!-- slot content --> </FancyButton> </template>
ผลลัพธ์ที่ได้
จะเห็นว่าส่วนที่เป็นข้อมูลใน slot content หรือข้อมูลสำหรับ slot จะถูกนำไปแทนในที
ส่วนของ <slot> แท็กที่อยู่ใน component นั่นคือทำให้เราสามารถส่งค่าใดๆ ที่ต้องการ
เพื่อไปแสดงยังส่วนนั้นได้ง่ายๆ เพียงเอาเนื้อหามาไว้ในส่วนของ slot content แล้วเนื้อหา
นั้นๆ ก็จะไปแสดงในส่วนของ slot outlet หรือ ทางออกหรือส่วนแสดงของ slot เป็นต้น
ดูรูปด้านล่างประกอบเพิ่มเติม
ซึ่งถ้าสมมติเราไม่ส่งค่าใดๆ เข้าไป ตรงปุ่ม ก็จะเป็นข้อความว่า "Default Button Text"
เช่น สมมติเราใช้เป็น
<template> <FancyButton> Click me! <!-- slot content --> </FancyButton> <br><br> <FancyButton> Save <!-- slot content --> </FancyButton> <br><br> <FancyButton></FancyButton> <br><br> <FancyButton /> <br><br> <fancy-button></fancy-button> </template>
ผลลัพธ์ก็ได้เป็น
การกำหนดชื่อของ Slot
เราสามารถกำหนดชื่อของ slot ที่ใช้งาน โดยใช้ attribute ที่ชื่อว่า name เป็นเหมือนกับ
การกำหนดไอดี ให้กับ slot นั้น ไว้อ้างอิงเรียกใช้งาน และสำหรับทุก slot ที่ไม่ได้กำหนดชื่อ
จะมีชื่อเป็นค่าเริ่มต้นเป็น default หรือมีค่า name="default" เป็นค่าเริ่มต้นถ้าไม่กำหนด
เพื่อให้เห็นภาพที่ชัดเจนขึ้น เราจะจำลองเหมือนกรณีเราสร้างเลเอาท์เว็บไซต์ ที่จะมีส่วนต่างๆ
เช่น ส่วน header main footer แล้วเราต้องการให้เนื้อหาของแต่ละส่วนเปลี่ยนตามค่า
ที่ถูกส่งเข้ามา เช่น ส่วน header ก็แสดงเนื้อหาส่วนบน footer ก็แสดงส่วนล่าง และ main
ก็แสดงเนื้อหาหลัก แบบนี้เป็นต้น
สร้างไฟล์ BaseLayout.vue ไว้ใน src > components
<script setup> import { ref } from 'vue' </script> <template> <div class="container"> <header> <slot name="header"></slot> <!-- We want header content here --> </header> <main> <slot></slot> <!-- We want main content here --> </main> <footer> <slot name="footer"></slot> <!-- We want footer content here --> </footer> </div> </template>
ส่วนของ slot ของ main เราไม่ได้กำหนดชื่อ จะมีชื่อเป็น name="default" อัตโนมัติ
เมื่อเราเรียกใช้งานในไฟล์ App.vue
<script setup> import { ref } from 'vue' import BaseLayout from './components/BaseLayout.vue' </script> <template> <BaseLayout> <template v-slot:header> <h1>This is header</h1> <!-- content for the header slot --> </template> <template v-slot> <h3>this is main</h3> </template> <template v-slot:footer> <h5>This is footer</h5> <!-- content for the header slot --> </template> </BaseLayout> </template>
เราสามารถทำการเรนเดอร์แยกส่วนโดยใช้ v-slot:ชื่อ slot หรือเขียนแบบย่อโดยใช้ # ตาม
ด้วยชื่อของ slot เช่น <template #header> หรือสำหรับ main ที่ไม่ได้กำหนดชื่อ
slot ก็จะเป็นชื่อ default เดิมใช้เป็น <template v-slot> ได้เป็น <template #default>
ดูตัวอย่างผลลัพธ์
รูปภาพอธิบายการทำงาน
การกำหนดเงื่อนไขการแสดง Slot
สมมติว่าในคอมโพเนนท์ เราต้องการให้ส่วนนั้นๆ แสดงหรือไม่ขึ้นอยู่กับการเรียกใช้ชื่อ slot
หรือไม่ ถ้าไม่มีการเรียกใช้ ก็ไม่ต้องแสดงส่วนของ template นั้น โดยวิธีนี้ เราใช้งาน v-if
แล้วตรวจสอบการมีอยู่ของการใช้งาน ชื่อ slot ผ่าน $slots property เช่น สมมติว่า
เราต้องการกำหนดเงื่อนไขว่าในคอมโพเนนท์นี้ ถ้าตัว parent ไม่ได้เรียกใช้งาน footer
ก็ไม่แสดง กล่าวคือ ถ้าเรียกใช้งานชื่อ footer เท่านั้น ส่วนนี้จึงจะแสดง สามารถปรับ
ไฟล์ BaseLayout.vue บางส่วนได้เป็นดังนี้
<script setup> import { ref } from 'vue' </script> <template> <div class="container"> <header> <slot name="header"></slot> <!-- We want header content here --> </header> <main> <slot></slot> <!-- We want main content here --> </main> <footer v-if="$slots.footer"> <slot name="footer"></slot> <!-- We want footer content here --> </footer> </div> </template>
ส่วนของ v-if="$slots.footer" หมายความว่า ถ้ามีการเรียกใช้ชื่อ slot ที่ชื่อ footer ส่วนนี้
จึงจะแสดง ถ้าไม่มีการเรียกใช้ ส่วนนี้ก็จะไม่แสดง นั่นก็คือส่วนของ <footer> จะไม่แสดง
เราจำลองการไม่เรียกใช้ชื่อ footer ในไฟล์ App.vue เป็นดังนี้
<script setup> import { ref } from 'vue' import BaseLayout from './components/BaseLayout.vue' </script> <template> <BaseLayout> <template v-slot:header> <h1>This is header</h1> <!-- content for the header slot --> </template> <template v-slot> <h3>this is main</h3> </template> <template v-slot:notfooter> <h5>This is footer</h5> <!-- content for the header slot --> </template> </BaseLayout> </template>
สังเกตว่า v-slot ตัวสุดท้าย เราเรียกใชังานเป็น notfooter หรือก็คือไม่มีการใช้งาน
footer ผลลัพธ์ที่ได้ก็คือจะไม่แสดงส่วนของ footer ตามรูปตัวอย่างด้านล่าง
สังเกตว่าเนื่องจากไม่มีการเรียกใช้งานชื่อ slot ที่ชื่อว่า footer แต่ไปเรียกใช้ชื่อ notfooter
ซึ่งไม่มีอยู่หรือไม่ได้กำหนด ทำให้ในส่วนของการเรนเดอร์ จะไม่มี <footer> แสดง แสดงเฉพาะ
ส่วนของ <header> และ <main>
เราสามารถกำหนดชื่อการเรียกใช้งาน slot name แบบ dynamic หรืออ้างอิงค่าตัวแปรได้
โดยวิธีก็คล้ายกับการใช้งาน attribute แบบ dynamic ที่เคยอธิบายมาก่อนหน้าแล้ว นั่นคือ
ให้ใช้เครื่องปีกกาสีเหลี่ยม แล้วกำหนดเป็นชื่อตัวแปร สำหรับใช้เป็นชื่อของ slot ที่จะเรียกใช้งาน
ตูตัวอย่างการใช้งาน
<base-layout> <template v-slot:[dynamicSlotName]> ... </template> <!-- with shorthand --> <template #[dynamicSlotName]> ... </template> </base-layout>
การเรียกใช้งาน Slot propterty จาก Parents
ปกติตามที่เราเข้าใจแต่ต้นคือ เราจะไม่สามารถเรียกใช้งาน property หรือ state ของ child
ที่เป็น component ได้ แต่จริงๆ แล้วเราสามารถเรียกใช้งานได้ผ่าน โดยตัว component จะ
ส่งผ่าน attrbute และตัว parent จะเรียกใช้งานผ่าน v-slot directive เรากลับมาที่ตัวอย่าง
ปุ่ม FancyButton ดูตัวอย่างดังนี้
<script setup> import { ref } from 'vue' const default_text = ref('Button') </script> <template> <button class="fancy-button"> <slot :text="default_text">Default Button Text</slot> </button> </template>
ในไฟล์คอมโพเนนท์ เรากำหนด state ให้ชื่อว่า default_text มีค่าเป็น 'Button' แล้วกำหนด
ในส่วนของ attribute ของ slot ใช้ชื่อว่า text ซึ่ง text ก็คือชื่อที่เราใช้สำหรับอ้างอิงเรียกใช้
งานค่าตัวแปร default_text ที่เป็น state ของคอมโพเนนท์ เราสามารถกำหนด attribute
ใดๆ ก็ได้นอกจาก text ยกเว้นคำว่า name จะเป็นชื่อสงวนไว้กำหนดชื่อ slot
เมื่อเรากำหนด text เป็น property ขอเรียกย่อๆ ว่า props ของ slot แล้ว ต่อไปก็ทำการ
เรียกใช้งานในส่วนของ parent
ไฟล์ App.vue
<script setup> import { ref } from 'vue' import FancyButton from './components/FancyButton.vue' </script> <template> <FancyButton v-slot="slotProps"> {{ slotProps.text }} </FancyButton> <br><br> <FancyButton></FancyButton> </template>
ผลลัพธ์ที่ได้
เราทดสอบแสดง 2 อัน โดยอันแรกมีการใช้งาน v-slot และกำหนดค่าโดยใช้ชื่อว่า slotProps
v-slot="slotProps" โดยชื่อนี้เรากำหนดเป็นอะไรก็ได้ แต่ต้องให้สอดคล้องกับที่กำลังใช้งาน
เพื่อจะได้เข้าใจง่าย จากนั้น เราก็สามารถเข้าถึง props ของ slot ที่ชื่อว่า text ได้ โดย text
จะดึงค่า state ของคอมโพเนนท์มาใช้อีกที ทำให้เราสามารถใช้คำว่า 'Button' มาใช้งานในส่วน
ของ Parent ได้ผ่านค่า {{ slotProps.text }}
ในขณะที่ปุ่มตัวอย่างที่ 2 เราไม่ได้กำหนดค่าอะไร ปุ่มก็จะเป็นค่าตามเดิม
ถ้าสมมติเราเพิ่มชื่อของ slot เข้าไป เช่นใช้ชื่อว่า button
<template> <button class="fancy-button"> <slot name="button" :text="default_text">Default Button Text</slot> </button> </template>
ในส่วนของ parent เราก็ต้องกำหนดชื่อด้วย
<FancyButton v-slot="slotProps"> {{ slotProps.text }} </FancyButton> // หรือเขียนแบบย่อ <FancyButton #default="slotProps"> {{ slotProps.text }} </FancyButton> // ก็เปลี่ยนเป็นแบบมีชื่อ <FancyButton v-slot:button="slotProps"> {{ slotProps.text }} </FancyButton> // หรือเขียนแบบย่อ <FancyButton #button="slotProps"> {{ slotProps.text }} </FancyButton> // เราสามารถเรียกใช้ props แบบ destructuring เพื่อให้เข้าถึงข้อมูลได้ง่ายได้ // ทำให้เวลาเรียกใช้งานก็จะสะดวกขึ้น <FancyButton #button="{ text }"> {{ text }} </FancyButton>
การใช้งาน Dynamic components
Dynamic components ใน Vue.js เป็นวิธีที่ช่วยให้เราสามารถเปลี่ยนแปลงคอมโพเนนต์
ที่แสดงผลใน template ได้แบบไดนามิก (dynamic) โดยไม่ต้องกำหนดล่วงหน้าใน HTML
ซึ่งช่วยให้การพัฒนามีความยืดหยุ่นและสามารถตอบสนองต่อ event ได้ง่ายขึ้น สามารถทำ
ได้โดยใช้ <component> และ :is
Vue.js มีแท็ก <component> ที่สามารถใช้เพื่อแสดงผลคอมโพเนนต์แบบไดนามิกโดยใช้
คุณสมบัติ :is เพื่อระบุชื่อของคอมโพเนนต์ที่ต้องการแสดงผล
สมมติเราสร้างคอมโพเนนท์ A และ คอมโพเนนท์ B ดังนี้
ComponentA.vue กับ ComponentB.vue ใน src > components
<template> <div>Component A</div> </template>
และ
<template> <div>Component B</div> </template>
จากนั้นในไฟล์ parents หรือ App.vue เรียกใช้เป็นดังนี้
<script setup> import { ref } from 'vue' import ComponentA from './components/ComponentA.vue' import ComponentB from './components/ComponentB.vue' const current = ref(0) const components = [ ComponentA,ComponentB ] function toggleComponent() { current.value = current.value == 0 ? 1 : 0 } </script> <template> <div> <component :is="components[current]"></component> <button @click="toggleComponent">Toggle Component</button> </div> </template>
ผลลัพธ์ที่ได้
เมื่อเราคลิกที่ปุ่ม จะเป็นการเปลียนค่า current ซึ่งเป็นค่า index หรือ key ของ components
ที่เรากำหนดเป้น array ค่าจะสลับไปมาระหว่าง 0 กับ 1 ทำให้ component จะสลับการแสดง
ตามเงื่อนไขของการใช้งาน :is="components[current]" นั่นคือ นำ component ที่ตรง
กับเงื่อนไขค่า components[current] เท่านั้นมาแสดง
รูปแบบการใช้งานลักษณะนี้ สามารถไปประยุกต์ใช้งานกับการกำหนด tab หรือ การกำหนดการ
แสดงส่วนของเนื้อหาได้ ชื่อ เราต้องการให้ส่วนอื่นๆ แสดงค่าเดิม แต่ต้องการให้เฉพาะส่วนนี้เปลี่ยน
แปลงค่าก็ใช้ <component> กับ :is ตามรูปแบบตัวอย่างข้างต้นได้
การใช้งานคอมโพเนนต์แบบไดนามิกควรใช้ในกรณีที่จำเป็น เพื่อไม่ให้โค้ดซับซ้อนเกินไป
Dynamic components เป็นฟีเจอร์ที่มีประโยชน์ในการสร้าง UI ที่ยืดหยุ่นและสามารถปรับ
เปลี่ยนได้ง่ายตามความต้องการของแอปพลิเคชัน
เนื้อหาเกี่ยวกับ Component เบื้องต้นตอนที่ 3 ก็ขอจบไว้เพียงเท่านี้ เนื้อหานี้อาจจะมี
การอธิบายเกี่ยวกับ slot ที่ค่อนข้างละเอียด เพราะถือว่ามีความสำคัญและมีประโยชน์
สำหรับเนื้อหาตอนหน้าจะเป็นอะไร รอติดตาม