Skip to content

Getting Started

Vue.js หรือ Nuxt.js ดี?

หากเริ่มต้นโปรเจคใหม่ nuxt.com faviconNuxt.js อาจเป็นตัวเลือกที่ดีกว่า เพราะมาพร้อมระบบ routing, SSR และ auto-imports ในตัว ช่วยประหยัดเวลาตั้งค่าและทำให้พัฒนาได้เร็วขึ้น แม้เพิ่งเริ่มต้น vuejs.org faviconVue.js ก็สามารถเรียนรู้ผ่าน Nuxt ได้เลย

FeatureVueNuxt
ประเภทเฟรมเวิร์กเฟรมเวิร์กที่สร้างบน Vue 🚀
สถาปัตยกรรมSingle Page App (SPA)SPA, SSR, SSG, Hybrid 🚀
Routingต้องตั้งค่าด้วยมืออัตโนมัติจากโครงสร้างไฟล์ ✅
โครงสร้างโปรเจคยืดหยุ่น ⚖️กำหนดโครงสร้างมาตรฐาน
SEOต้องตั้งค่าเพิ่มพร้อมใช้งานทันที ✅
Server-Side Renderingต้องตั้งค่าเองพร้อมใช้งาน ✅
Static Site Generationต้องใช้ pluginพร้อมใช้งาน ✅
การตั้งค่าต่ำกว่า เรียนรู้ง่าย ✅สูงกว่า ต้องเรียนรู้เพิ่ม
เหมาะสำหรับแอปขนาดเล็ก-กลาง ✅แอปขนาดใหญ่, เว็บไซต์ ✅
ชุมชนใหญ่ ✅ขนาดกลาง (แต่เติบโตเร็ว)
เอกสารละเอียด ✅ดี แต่มีน้อยกว่า Vue
การพัฒนาเร็วเร็ว ✅เร็วแต่ต้องเรียนรู้เพิ่ม
ความปลอดภัยดีดีกว่า มีฟีเจอร์ป้องกันเพิ่ม ✅
การทดสอบต้องตั้งค่าเองมีระบบทดสอบในตัว ✅

Basic Concepts

Components (for UI)

ส่วนประกอบพื้นฐานของ Vue ที่ใช้สร้าง UI โดยแบ่งเป็นไฟล์ .vue แยกกัน

ส่วนประกอบของ components

Type of StateคำอธิบายExampleกรณีการใช้งาน
Propsค่าที่รับเข้ามาในคอมโพเนนต์interface Props { msg: string }ส่งข้อมูลจาก Parent ไปยัง Child component
Emitsเหตุการณ์ที่ส่งออกจากคอมโพเนนต์(e: 'update', value: string): voidแจ้ง Parent เมื่อมีการเปลี่ยนแปลงใน Child
Slotsช่องสำหรับส่งเนื้อหา HTML/Component<template #header>...</template>สร้าง Component ที่มีโครงสร้างยืดหยุ่น
Provide/Injectส่งค่าข้ามหลายระดับคอมโพเนนต์provide('key', value) / inject('key')ส่งข้อมูลระหว่าง Component ที่ซ้อนกันหลายชั้น

1. Props - ส่งข้อมูลจาก Parent ไป Child

vue
<!-- ตัวอย่างการใช้ Props -->
<template>
  <!-- ส่ง props ชื่อ msg ไปยัง ChildComponent -->
  <ChildComponent :msg="message" />
</template>

<script setup lang="ts">
import ChildComponent from './ChildComponent.vue'

// ข้อมูลที่จะส่งเป็น props
const message = ref('สวัสดีจาก Parent')
</script>
vue
<template>
  <!-- รับและแสดง props ที่ชื่อ msg -->
  <div>{{ msg }}</div>
</template>

<script setup lang="ts">
// กำหนดประเภทของ props ที่จะรับ
interface Props {
  msg: string
}

// รับ props
const props = defineProps<Props>()
</script>

2. Emits - ส่งเหตุการณ์จาก Child ไป Parent

vue
<!-- ตัวอย่างการใช้ Emits -->
<template>
  <!-- เมื่อคลิกปุ่มจะส่ง event 'update' พร้อมค่า newValue -->
  <button @click="$emit('update', newValue)">อัปเดต</button>
</template>

<script setup lang="ts">
// กำหนดประเภทของ events ที่จะส่ง
const emit = defineEmits<{
  (e: 'update', value: string): void
}>()

const newValue = 'ข้อมูลที่อัปเดตแล้ว'
</script>
vue
<template>
  <!-- รับฟัง event 'update' จาก Child -->
  <ChildComponent @update="handleUpdate" />
</template>

<script setup lang="ts">
// ฟังก์ชันที่ทำงานเมื่อได้รับ event 'update'
const handleUpdate = (value: string) => {
  console.log('ได้รับค่า:', value)
}
</script>

3. Slots - ส่งเนื้อหา HTML/Component

vue
<template>
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot> <!-- Default slot -->
    </main>
  </div>
</template>
vue
<template>
  <BaseLayout>
    <template #header>
      <h1>Page Title</h1>
    </template>
    
    <p>Main content goes here</p>
  </BaseLayout>
</template>

4. Provide/Inject - ส่งค่าข้ามหลายระดับ Component

vue
<template>
  <ChildComponent />
</template>

<script setup lang="ts">
import { provide } from 'vue'

provide('theme', 'dark')
</script>
vue
<template>
  <div :class="theme">...</div>
</template>

<script setup lang="ts">
import { inject } from 'vue'

const theme = inject('theme', 'light') // Default value 'light'
</script>

Composables (for Refactoring)

ฟังก์ชันที่ใช้แยกและจัดระเบียบ logic ใน Vue

Patternตัวอย่างใช้เมื่อ
State ManagementuseCounter()ต้องการแยก logic การนับ
API CallsuseFetch()การดึงข้อมูลจาก API
UI LogicuseModal()การจัดการ modal/popup
ts
// useCounter.ts
export default function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  const reset = () => count.value = initialValue
  
  return { count, increment, decrement, reset }
}
ts
// ใน component
const { count, increment } = useCounter()

Best Practices:

  1. ตั้งชื่อไฟล์เป็น useXXX.ts
  2. ใช้ ref/reactive สำหรับ state
  3. Return object ที่มี methods และ state
  4. ใช้ TypeScript interface เมื่อจำเป็น

Lifecycle (for Hooks)

วงจรชีวิตของคอมโพเนนต์ ตั้งแต่สร้างจนถึงถูกทำลาย

Hookเรียกเมื่อตัวอย่างการใช้งาน
onBeforeMountก่อนที่ component จะถูก mountตรวจสอบ authentication, โหลด config จาก localStorage
onMountedหลังจาก component ถูก mountดึงข้อมูล, ตั้งค่า event listeners, ทำ animation
onBeforeUpdateก่อนที่ DOM จะอัปเดตบันทึกตำแหน่ง scroll, เปรียบเทียบค่าเก่า/ใหม่
onUpdatedหลังจาก DOM อัปเดตอัปเดต chart/library, ปรับขนาด/ตำแหน่ง
onBeforeUnmountก่อนที่ component จะถูก unmountลบ event listeners, ยกเลิก timers
onUnmountedหลังจาก component ถูก unmountยกเลิก subscriptions, ปิด connections
onErrorCapturedเมื่อเกิด error ใน componentแสดงข้อความ error ที่เข้าใจง่าย, ส่ง error logs
onRenderTrackedเมื่อ render ของ component ถูกติดตามตรวจสอบการเปลี่ยนแปลงของ props หรือ state
onRenderTriggeredเมื่อ render ของ component ถูกเรียกตรวจสอบว่า render เกิดจาก props, state หรืออื่นๆ

ตัวอย่างการใช้งาน Composition API lifecycle:

vue
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'

// ประกาศตัวแปร reactive
const count = ref(0)
// ประกาศตัวแปรเก็บ timer
let timer: number

// เมื่อ component ถูก mount
onMounted(() => {
  console.log('Component mounted')
  // ตั้งค่า timer ให้เพิ่มค่า count ทุกๆ 1 วินาที
  timer = setInterval(() => {
    count.value++
  }, 1000)
})

// เมื่อ component ถูก unmount
onUnmounted(() => {
  console.log('Component unmounted')
  // ล้าง timer เมื่อ component ถูกลบ
  clearInterval(timer)
})
</script>

<template>
  <div>
    <!-- แสดงค่าของ count -->
    <p>Count: {{ count }}</p>
  </div>
</template>

Styling (for CSS)

การจัดการสไตล์และ CSS ใน Vue Components

วิธีการคำอธิบายเมื่อไหร่ที่ควรใช้
Scoped Styles<style scoped> สไตล์จะมีผลเฉพาะคอมโพเนนต์ปัจจุบันวิธีเริ่มต้นสำหรับสไตล์เฉพาะคอมโพเนนต์
Utility FrameworksTailwind/UnoCSS คลาส utility แบบ atomicการสร้างเร็ว, โปรเจคขนาดเล็ก-กลาง
Dynamic Classesการผูก :class กำหนดคลาสตามเงื่อนไขการกำหนดสไตล์แบบไดนามิกตามสถานะของคอมโพเนนต์
vue
<template>
  <div class="p-4 text-red-500 hover:text-blue-500 transition">
    <button class="px-4 py-2 bg-blue-500 rounded text-white">
      Click Me
    </button>
  </div>
</template>
vue
<template>
  <div 
    :class="{
      'text-red-500': isError,
      'font-bold': isImportant
    }">
    Dynamic Content
  </div>
</template>

<script setup>
const isError = ref(true)
const isImportant = ref(false)
</script>

Template (for Rendering)

ไวยากรณ์พื้นฐานใน template ของ Vue สำหรับการเรนเดอร์

Directives

Conceptตัวอย่างกรณีการใช้งาน
vuejs.org faviconv-if/v-show<div v-if="show">การแสดงผลแบบมีเงื่อนไข
vuejs.org faviconv-for<li v-for="item in items">การแสดงรายการข้อมูล
vuejs.org faviconv-bind:class="{ active: isActive }"การกำหนด attribute แบบไดนามิก
vuejs.org faviconv-on@click="handleClick"การจัดการเหตุการณ์
vue
<template>
  <div>
    <p v-if="showMessage">This appears when showMessage is true</p>
    <div v-show="isVisible">This toggles visibility</div>
  </div>
</template>
vue
<template>
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      {{ index + 1 }}. {{ item.name }}
    </li>
  </ul>
</template>
vue
<template>
  <div 
    :class="{ active: isActive, 'text-red': hasError }"
    :style="{ fontSize: size + 'px' }"
  >
    Dynamic Styles
  </div>
</template>
vue
<template>
  <button 
    @click="handleClick"
    @mouseover="onHover"
    @keyup.enter="submit"
  >
    Interactive Button
  </button>
</template>

Components

Conceptตัวอย่างกรณีการใช้งาน
vuejs.org faviconSlots<slot></slot>การประกอบคอมโพเนนต์
vuejs.org faviconDynamic Components<component :is="activeTab">การสลับคอมโพเนนต์
vuejs.org faviconTransition<transition name="fade">เอฟเฟกต์การเปลี่ยนภาพ
vue
<!-- Parent -->
<template>
  <ChildComponent>
    <template #header>
      <h1>Custom Header</h1>
    </template>
    Default Slot Content
  </ChildComponent>
</template>

<!-- Child -->
<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
  </div>
</template>
vue
<template>
  <component :is="currentView" />
</template>

<script setup>
const currentView = shallowRef(Home)
</script>
vue
<template>
  <transition name="fade" mode="out-in">
    <div v-if="show" key="content">
      Content to animate
    </div>
  </transition>
</template>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

State (for Data)

การจัดการสถานะข้อมูลในแอปพลิเคชัน Vue

Type of Stateใช้สำหรับตัวอย่างการใช้งาน
pinia.vuejs.org faviconGlobal State (Pinia)การจัดการ state ร่วมกันหลาย componentsconst store = useStore()
vuejs.org faviconLocal Stateการจัดการ state ใน component เดียวconst count = ref(0)
ts
// สร้าง store
const store = useStore()

// ใช้ state จาก store
const products = computed(() => store.products)

// เรียก action
const loadProducts = async () => {
  await store.loadProducts()
}
ts
// สร้าง reactive state
const searchQuery = ref('') // สำหรับ primitive values
const user = reactive({    // สำหรับ objects
  name: 'John',
  age: 30,
  preferences: {
    darkMode: true
  }
})

// ใช้ computed สำหรับ derived state
const isAdult = computed(() => user.age >= 18)

WARNING

  • อย่าแก้ไข state โดยตรงนอกเหนือจาก action ใน Pinia
  • สำหรับ Pinia ควรใช้ action เท่านั้นในการแก้ไข state
  • ใช้ markRaw เมื่อต้องการเก็บ object ที่ไม่ต้องการให้เป็น reactive

TIP

  • ใช้ computed สำหรับค่าที่คำนวณจาก state อื่น
  • ใช้ watchEffect สำหรับ side effects ที่ขึ้นกับ reactive state

Events (for Interaction)

ระบบจัดการเหตุการณ์และ interaction ในคอมโพเนนต์

FeatureคำอธิบายExampleกรณีการใช้งาน
Native Eventsรับฟัง DOM events ด้วย v-on หรือ @@click="handleClick"การจัดการกับ DOM events
Custom Eventsการสื่อสารจาก child ไป parent ด้วย $emitthis.$emit('custom-event')การสื่อสารระหว่าง components
Event Modifiersคำต่อท้ายสำหรับจัดการ event ทั่วไป@click.stop.preventการจัดการกับ event bubbling
Key Modifiersรับฟังปุ่มคีย์บอร์ดเฉพาะ@keyup.enter="submit"การใช้งานกับ keyboard shortcuts
Mouse Modifiersevent ปุ่มเมาส์เฉพาะ@click.right="contextMenu"การใช้งานกับ context menu
System Modifiersปุ่ม Ctrl, Alt, Shift, Meta@click.ctrl="shortcut"การใช้งานกับ keyboard shortcuts
vue
<template>
  <!-- Native Event ตัวอย่างการใช้งาน event พื้นฐาน -->
  <button @click="handleClick">
    Click Me
  </button>
  
  <form @submit.prevent="handleSubmit">
    <input @input="onInput" />
  </form>
</template>
vue
<!-- Child -->
<template>
  <button @click="$emit('custom-event', data)">
    Send Event
  </button>
</template>

<!-- Parent -->
<template>
  <ChildComponent @custom-event="handleEvent" />
</template>
vue
<template>
  <!-- Stop event propagation -->
  <div @click.stop="doSomething"></div>
  
  <!-- Prevent default -->
  <form @submit.prevent="submit"></form>
  
  <!-- Key modifier -->
  <input @keyup.enter="submit" />
</template>

APIs

Global APIs

APIsคำอธิบายตัวอย่างการใช้งานกรณีการใช้งาน
vuejs.org faviconcreateApp()เริ่มต้นแอป Vue ใหม่const app = createApp(App)เมื่อต้องการสร้างแอป Vue ใหม่
vuejs.org faviconnextTick()รอการอัปเดต DOM เสร็จสิ้นก่อนทำงานต่อnextTick(() => { /* DOM updated */ })เมื่อต้องทำงานกับ DOM หลังการอัปเดตข้อมูล
vuejs.org faviconversionตรวจสอบเวอร์ชัน Vue ที่ใช้งานอยู่console.log(Vue.version)เมื่อต้องการตรวจสอบความเข้ากันได้ของฟีเจอร์
vuejs.org favicondefineComponent()กำหนดคอมโพเนนต์พร้อมการตรวจสอบประเภทข้อมูลexport default defineComponent({...})เมื่อสร้างคอมโพเนนต์ใน TypeScript

Single File Components

ฟีเจอร์วิธีใช้งาน
vuejs.org faviconscript setupเขียน logic ในแท็ก <script setup>
vuejs.org faviconCSS Scopingเพิ่ม scoped ในแท็ก <style>
vuejs.org faviconCustom Blocksกำหนดแท็ก <docs> หรือ <tests>

Built-in Components

APIsคำอธิบายตัวอย่างการใช้งานกรณีการใช้งาน
vuejs.org faviconTransitionจัดการการเปลี่ยนระหว่างสถานะ<Transition name="fade">...</Transition>เมื่อต้องการ animation ระหว่างการแสดง/ซ่อน
vuejs.org faviconKeepAliveเก็บสถานะคอมโพเนนต์เมื่อซ่อน<KeepAlive><Component /></KeepAlive>เมื่อต้องการเก็บสถานะระหว่างการสลับแท็บ
vuejs.org faviconTeleportย้าย element ไปตำแหน่งอื่นใน DOM<Teleport to="#modals"><Modal /></Teleport>เมื่อต้องการแสดง component นอก hierarchy ปกติ
vuejs.org faviconSuspenseแสดง loading รอข้อมูล<Suspense><AsyncComponent /></Suspense>เมื่อโหลด component แบบ asynchronous

Composition API

ฟังก์ชันคำอธิบายตัวอย่างการใช้งาน
vuejs.org faviconref()/reactive()สร้าง reactive references หรือ objectsconst count = ref(0)
vuejs.org faviconcomputed()/watch()computed properties และ watchersconst double = computed(() => count.value * 2)
vuejs.org faviconprovide()/inject()Dependency injection สำหรับคอมโพเนนต์provide('key', value) / const val = inject('key')
vuejs.org faviconLifecycle Hookslifecycle hooks สำหรับคอมโพเนนต์onMounted(() => { console.log('mounted') })
vuejs.org favicontoRef()แปลง property ของ reactive object เป็น refconst name = toRef(user, 'name')
vuejs.org favicontoRefs()แปลง reactive object เป็น plain object ของ refsconst { name, age } = toRefs(user)

Advanced Patterns

APIsคำอธิบายตัวอย่างการใช้งานกรณีการใช้งาน
vuejs.org faviconCustom Directivesdirectives กำหนดเองสำหรับจัดการ DOMv-focus สำหรับ auto-focus inputเมื่อต้องการจัดการ DOM โดยตรง
vuejs.org faviconPluginsplugins สำหรับฟีเจอร์ระดับ globalapp.use(myPlugin)เมื่อต้องการเพิ่มฟีเจอร์ที่ใช้ทั่วทั้งแอป
vuejs.org faviconRender Functionsฟังก์ชันสำหรับการ render แบบกำหนดเองh('div', { id: 'foo' }, 'hello')เมื่อต้องการความยืดหยุ่นในการ render สูง
vuejs.org faviconJSXไวยากรณ์ JSX สำหรับการ renderconst vnode = <div id="foo">{hello}</div>เมื่อต้องการเขียน template แบบ JavaScript

Best Practices

Naming Convention

TypeNaming ConventionExample
ComponentsPascalCaseUserProfile.vue
Composition Functionsuse + camelCaseuseFetchData
PropscamelCase (script), kebab-case (template)userName, user-name
Eventskebab-case@update-value
Pinia Storesuse + Store + camelCaseuseUserStore

Performance

Rendering Optimization

TechniqueคำอธิบายExample
Conditional Renderingv-if: ลบออกจาก DOM เมื่อเป็น false
v-show: ซ่อนด้วย CSS สำหรับการสลับบ่อยครั้ง
<div v-if="show">
<div v-show="active">
List Renderingใช้ ID ที่ไม่ซ้ำกันเป็น key สำหรับการแสดงผลรายการที่มีประสิทธิภาพ<li v-for="item in items" :key="item.id">
Virtual Scrollingrender only visible parts, reduce memory usage<VirtualScroller :items="bigData" />

Data Handling

TechniqueคำอธิบายExample
Computedcache value for dependenciesconst total = computed(() => price * qty)
Shallow Refsdisable deep reactivity when not neededconst bigObj = shallowRef({...})
Debouncelimit frequent function calls<input @input="debounceFn">

Code Splitting

TechniqueคำอธิบายExample
Lazy Loadload components on demandconst Modal = defineAsyncComponent(() => import('./Modal.vue'))
Route Splitsplit code by route for smaller initial bundlecomponent: () => import('./UserProfile.vue')

Type Safety

วิธีปฏิบัติประโยชน์ตัวอย่าง
เปิดโหมด Strictตรวจสอบประเภทข้อมูลอย่างเข้มงวด"strict": true ใน tsconfig.json
กำหนด Custom Typesลดข้อผิดพลาดจากโครงสร้างข้อมูลinterface User { name: string }
ใช้ Utility Typesเพิ่มความยืดหยุ่นในการใช้งานPartial<User> สำหรับข้อมูลบางส่วน

Component Organization

vue
<!-- MyComponent.vue -->
<script setup lang="ts">
// 1. Imports (external libraries)
import { ref } from 'vue'
import { useRouter } from 'vue-router'

// 2. Type Definitions
interface Props {
  id: number
  title: string
}

// 3. Component Props & Emits
defineProps<Props>()
const emit = defineEmits<{
  (e: 'submit', value: string): void
}>()

// 4. Composables
const { user } = useAuth()

// 5. Reactive State
const count = ref(0)
const isLoading = ref(false)

// 6. Lifecycle Hooks
onMounted(() => {
  fetchData()
})

// 7. Methods/Functions
async function fetchData() {
  isLoading.value = true
  // ...
  isLoading.value = false
}
</script>

<template>
  <!-- 
    Template Organization:
    1. Root element (1 element only)
    2. Conditional Rendering (v-if/v-show)
    3. List Rendering (v-for)
    4. Event Handlers 
    5. Dynamic Classes/Styles
  -->
  <div class="container">
    <h1>{{ title }}</h1>
    <button @click="emit('submit', 'data')">
      Submit
    </button>
  </div>
</template>

<style scoped>
/* 
  CSS Organization:
  1. Layout (position, grid, flex)
  2. Box Model (margin, padding, border)
  3. Typography 
  4. Visual (colors, shadows)
  5. Animations
*/
.container {
  padding: 1rem;
}
</style>

Best Practices:

  1. Order script sections by importance (top-down)
  2. Separate template sections with comments
  3. Group CSS by purpose
  4. Use meaningful names for variables and functions

Project Structure

Project
ts
// 1. Atomic Design Structure
src/
├── components/
│   ├── atoms/        // Button, Icon, Input
│   ├── molecules/    // SearchBar, Card
│   ├── organisms/    // Header, ProductGrid
│   └── templates/    // MainLayout, AuthLayout

// 2. Feature-based Structure
src/
├── features/
│   ├── auth/         // LoginForm, RegisterForm
│   ├── dashboard/    // StatsCard, Chart
│   └── products/     // ProductList, ProductDetail

// 3. Core Files
src/
├── App.vue          // Root component
├── main.ts          // App entry
└── vite.config.ts   // Build config

Last updated: