# คำถามสัมภาษณ์ Vue.js: 25 ข้อสำคัญเพื่อคว้างาน > เตรียมสัมภาษณ์ Vue.js ด้วย 25 คำถามสำคัญ ตั้งแต่ระบบ reactivity ถึง composables เพื่อพิชิตการสัมภาษณ์ครั้งต่อไป - Published: 2026-02-05 - Updated: 2026-04-27 - Author: SharpSkill - Tags: vue.js, interview, frontend, javascript, technical questions - Reading time: 15 min --- การสัมภาษณ์ Vue.js วัดผลมากกว่าแค่ไวยากรณ์ของเฟรมเวิร์ก ผู้สรรหาต้องการเห็นความเข้าใจระบบ reactivity ความสามารถในการจัดระเบียบโค้ดด้วย Composition API และทักษะการแก้ปัญหาด้านประสิทธิภาพและสถาปัตยกรรมที่เกิดขึ้นจริง > **คำแนะนำในการเตรียมตัว** > > ทุกคำถามมาพร้อมคำตอบโดยละเอียดและตัวอย่างโค้ด สำหรับการสัมภาษณ์ทางเทคนิค การฝึกอธิบายแนวคิดออกเสียงเหมือนอยู่ในห้องสัมภาษณ์จริงจะช่วยได้มาก ## คำถามพื้นฐานเกี่ยวกับ Vue.js ### 1. ref และ reactive ใน Vue 3 ต่างกันอย่างไร คำถามนี้ทดสอบความเข้าใจระบบ reactivity ซึ่งเป็นหัวใจของ Vue 3 ความแตกต่างหลักอยู่ที่ประเภทข้อมูลและรูปแบบการเข้าถึง `ref` สร้างการอ้างอิงแบบ reactive สำหรับค่าพื้นฐาน (string, number, boolean) และต้องใช้ `.value` เพื่อเข้าถึงค่าภายในสคริปต์ ส่วน `reactive` สร้าง proxy แบบ reactive สำหรับ object และเข้าถึงคุณสมบัติได้โดยตรง ```javascript // ตัวอย่างเปรียบเทียบ ref กับ reactive import { ref, reactive } from 'vue' // ref: for primitives // Requires .value in the script const count = ref(0) count.value++ // Access with .value // reactive: for complex objects // Direct property access const user = reactive({ name: 'Alice', age: 25 }) user.age++ // No .value needed // Warning: reactive loses reactivity if reassigned // user = { name: 'Bob', age: 30 } // ❌ Breaks reactivity Object.assign(user, { name: 'Bob', age: 30 }) // ✅ Correct ``` หลักทั่วไป: ใช้ `ref` กับค่าธรรมดา และ `reactive` กับ object ที่มีหลายคุณสมบัติเชื่อมโยงกัน ### 2. ระบบ reactivity ของ Vue 3 ทำงานอย่างไร Vue 3 ใช้ Proxy ของ JavaScript (ES6) เพื่อดักจับการดำเนินการบน object แบบ reactive ต่างจาก Vue 2 ที่ใช้ `Object.defineProperty` วิธีนี้สามารถตรวจจับการเพิ่มและลบคุณสมบัติแบบไดนามิก ```javascript // Simplified demonstration of the reactivity principle // Vue uses Proxies to track dependencies const handler = { // Intercept reading get(target, key, receiver) { track(target, key) // Register the dependency return Reflect.get(target, key, receiver) }, // Intercept writing set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver) trigger(target, key) // Trigger updates return result } } // Creating a reactive proxy const reactiveObject = new Proxy(originalObject, handler) ``` ข้อดีของ Proxy ได้แก่ การตรวจจับคุณสมบัติใหม่ การรองรับ Map และ Set รวมถึงประสิทธิภาพที่ดีขึ้นกับ object ขนาดใหญ่ ### 3. อธิบายความต่างระหว่าง computed และ watch `computed` และ `watch` ตอบโจทย์การจัดการ reactivity ที่ต่างกัน **Computed**: คำนวณค่าที่เป็นผลจากข้อมูล reactive อื่น ค่าจะถูก cache ไว้และคำนวณใหม่เฉพาะตอนที่ dependency เปลี่ยน เหมาะกับการแปลงข้อมูล **Watch**: รัน side effect เพื่อตอบสนองต่อการเปลี่ยนแปลง ใช้สำหรับการเรียก API การโต้ตอบกับ DOM หรือการดำเนินการแบบอะซิงโครนัส ```javascript // Computed vs watch comparison import { ref, computed, watch } from 'vue' const firstName = ref('John') const lastName = ref('Doe') // computed: derived value with cache // Recalculates only if firstName or lastName changes const fullName = computed(() => { console.log('Computing full name') // Called only once return `${firstName.value} ${lastName.value}` }) // Multiple accesses = single execution (cached) console.log(fullName.value) // "John Doe" console.log(fullName.value) // No recalculation // watch: side effect without cache // Executed on every change watch(firstName, async (newName, oldName) => { // Side effect: API call await saveToServer({ firstName: newName }) console.log(`Name changed from ${oldName} to ${newName}`) }) ``` ### 4. Virtual DOM คืออะไร และ Vue ใช้งานอย่างไร Virtual DOM คือการแทน DOM จริงในรูปแบบ JavaScript ที่เบา Vue เก็บโครงสร้างต้นไม้เสมือนไว้ในหน่วยความจำและคำนวณส่วนต่าง (diffing) ระหว่างสถานะเก่ากับใหม่ เพื่ออัปเดตเฉพาะสิ่งที่จำเป็นบน DOM จริง ```javascript // Conceptual representation of the Virtual DOM // Vue creates this structure internally const vnode = { type: 'div', props: { class: 'container', id: 'app' }, children: [ { type: 'h1', props: {}, children: 'Title' }, { type: 'p', props: {}, children: 'Paragraph content' } ] } // When changes occur, Vue compares vnodes // and updates only the modified elements ``` การเพิ่มประสิทธิภาพของ Vue 3 ได้แก่ การ hoist โหนดสแตติก patch flag เพื่อระบุประเภทการเปลี่ยนแปลง และ tree-shaking ในคอมไพเลอร์ ### 5. จัดการการสื่อสารระหว่างคอมโพเนนต์ที่ไม่ได้สัมพันธ์โดยตรงอย่างไร มีหลายรูปแบบสำหรับให้คอมโพเนนต์ที่ไม่ได้มีความสัมพันธ์ parent-child โดยตรงคุยกัน ```javascript // Solution 1: Event Bus (small applications) // eventBus.js import { ref } from 'vue' const bus = ref(new Map()) export function useEventBus() { // Emit an event const emit = (event, payload) => { const callbacks = bus.value.get(event) || [] callbacks.forEach(cb => cb(payload)) } // Listen to an event const on = (event, callback) => { if (!bus.value.has(event)) { bus.value.set(event, []) } bus.value.get(event).push(callback) } return { emit, on } } ``` ```javascript // Solution 2: Provide/Inject for nested components // Ancestor import { provide, ref } from 'vue' const sharedState = ref('shared value') provide('stateKey', sharedState) // Descendant (any level) import { inject } from 'vue' const state = inject('stateKey') ``` สำหรับแอปพลิเคชันที่ซับซ้อนมากขึ้น Pinia ยังเป็นทางเลือกที่แนะนำสำหรับการจัดการ state แบบ global ## คำถามเกี่ยวกับ Composition API ### 6. Composition API มีข้อได้เปรียบอย่างไรเมื่อเทียบกับ Options API Composition API มีจุดเด่นเชิงโครงสร้างหลายอย่างเมื่อเทียบกับ Options API ดั้งเดิม **จัดระเบียบตามฟีเจอร์**: โค้ดที่เกี่ยวข้องกับฟีเจอร์เดียวกันถูกจัดวางอยู่ด้วยกัน ในขณะที่ Options API จะแยกตามประเภท (data, methods, computed) **นำกลับมาใช้ใหม่ผ่าน composables**: ดึงตรรกะออกเป็นฟังก์ชันที่นำกลับมาใช้ได้ง่าย **รองรับ TypeScript ดีขึ้น**: การ infer ประเภทเป็นไปอย่างเป็นธรรมชาติ ไม่ต้องใช้ decorator ```javascript // Options API: code fragmented by type export default { data() { return { searchQuery: '', results: [] } }, computed: { hasResults() { return this.results.length > 0 } }, methods: { async search() { this.results = await fetchResults(this.searchQuery) } }, watch: { searchQuery: 'search' } } // Composition API: code grouped by feature import { ref, computed, watch } from 'vue' export function useSearch() { const searchQuery = ref('') const results = ref([]) const hasResults = computed(() => results.value.length > 0) const search = async () => { results.value = await fetchResults(searchQuery.value) } watch(searchQuery, search) return { searchQuery, results, hasResults, search } } ``` ### 7. สร้าง composable ที่นำกลับมาใช้ใหม่ได้อย่างไร composable คือฟังก์ชันที่ห่อตรรกะ reactive ไว้ ข้อตกลงทั่วไป: ใช้คำนำหน้า `use` คืน object ที่มีทั้ง state และ method และจัดการ cleanup ```javascript // composables/useLocalStorage.js import { ref, watch } from 'vue' // Composable to synchronize state with localStorage export function useLocalStorage(key, defaultValue) { // Retrieve initial value from localStorage const storedValue = localStorage.getItem(key) const data = ref( storedValue ? JSON.parse(storedValue) : defaultValue ) // Synchronize changes to localStorage watch( data, (newValue) => { if (newValue === null) { localStorage.removeItem(key) } else { localStorage.setItem(key, JSON.stringify(newValue)) } }, { deep: true } // Observe nested objects ) return data } // Usage in a component const theme = useLocalStorage('theme', 'light') const userPrefs = useLocalStorage('prefs', { notifications: true }) ``` > **ข้อตกลงการตั้งชื่อ** > > composable ใช้รูปแบบ `useXxx` เพื่อบ่งบอกถึงคุณสมบัติที่นำกลับมาใช้ได้ ข้อตกลงนี้ทำให้โค้ดอ่านง่ายและช่วยให้สังเกต dependency แบบ reactive ได้ง่ายขึ้น ### 8. อธิบาย watchEffect เทียบกับ watch `watchEffect` กับ `watch` ต่างก็ตอบสนองต่อการเปลี่ยนแปลง แต่แนวทางแตกต่างกัน **watchEffect**: ทำงานทันทีและรันใหม่อัตโนมัติเมื่อ dependency แบบ reactive เปลี่ยน การติดตาม dependency เป็นไปโดยอัตโนมัติ **watch**: คอยสังเกตแหล่งข้อมูลที่ระบุและให้ค่าเก่ากับค่าใหม่ มีอำนาจในการกำหนดเวลาที่จะถูกกระตุ้นมากกว่า ```javascript // watchEffect vs watch comparison import { ref, watch, watchEffect } from 'vue' const userId = ref(1) const userData = ref(null) // watchEffect: automatic tracking // Runs immediately watchEffect(async () => { // userId is automatically tracked const response = await fetch(`/api/users/${userId.value}`) userData.value = await response.json() }) // watch: explicit sources with old values watch(userId, async (newId, oldId) => { console.log(`User changed from ${oldId} to ${newId}`) const response = await fetch(`/api/users/${newId}`) userData.value = await response.json() }, { immediate: true // Run immediately like watchEffect }) // watchEffect with cleanup watchEffect((onCleanup) => { const controller = new AbortController() fetch(`/api/users/${userId.value}`, { signal: controller.signal }).then(/* ... */) // Cleanup: cancel previous request onCleanup(() => controller.abort()) }) ``` ### 9. จัดการ props กับ TypeScript ใน script setup อย่างไร ไวยากรณ์ ` ``` ## คำถามด้านประสิทธิภาพ ### 10. มีเทคนิคการเพิ่มประสิทธิภาพอะไรบ้าง Vue 3 มีกลไกหลายอย่างในการเพิ่มประสิทธิภาพ ```javascript // 1. v-once: single render for static content // 2. v-memo: conditional memoization {{ item.name }} // 3. shallowRef/shallowReactive: shallow reactivity import { shallowRef, triggerRef } from 'vue' // Only tracks ref replacement, not internal mutations const largeList = shallowRef([/* thousands of elements */]) // Force update after mutation largeList.value.push(newItem) triggerRef(largeList) // Manually trigger re-render ``` ```javascript // 4. Async components for code-splitting import { defineAsyncComponent } from 'vue' const HeavyComponent = defineAsyncComponent({ loader: () => import('./HeavyComponent.vue'), loadingComponent: LoadingSpinner, delay: 200, // Delay before showing loading errorComponent: ErrorDisplay, timeout: 3000 }) // 5. KeepAlive for component caching ``` ### 11. หลีกเลี่ยงการ re-render ที่ไม่จำเป็นได้อย่างไร การ re-render ที่ไม่จำเป็นส่งผลต่อประสิทธิภาพ มีหลายแนวทางที่ช่วยลดให้น้อยที่สุด ```javascript // Problem: function created on each render handleClick(item.id)" /> // Solution: use a method or ref ``` ```javascript // Using computed for expensive calculations import { computed } from 'vue' // ❌ Recalculated on every render const getFilteredItems = () => { return items.value.filter(/* complex logic */) } // ✅ Cached, recalculated only if items changes const filteredItems = computed(() => { return items.value.filter(/* complex logic */) }) ``` ### 12. อธิบาย lazy loading ของคอมโพเนนต์และ route Lazy loading ทำให้โหลดโค้ดเมื่อจำเป็น ลดขนาด bundle ตอนเริ่มต้น ```javascript // router/index.js import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', // Immediate loading (main bundle) component: () => import('@/views/Home.vue') }, { path: '/dashboard', // Separate chunk with custom name component: () => import( /* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue' ), // Lazy loading child routes children: [ { path: 'analytics', component: () => import('@/views/Analytics.vue') } ] }, { path: '/admin', // Prefetch on link hover component: () => import('@/views/Admin.vue'), meta: { prefetch: true } } ] }) export default router ``` ## คำถามเกี่ยวกับ Vue Router ### 13. ป้องกัน route ด้วย guard อย่างไร Navigation guard ใช้ควบคุมการเข้าถึง route ```javascript // router/index.js import { createRouter } from 'vue-router' import { useAuthStore } from '@/stores/auth' const router = createRouter({ routes: [ { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true, roles: ['admin', 'user'] } } ] }) // Global guard: checks authentication router.beforeEach(async (to, from, next) => { const auth = useAuthStore() // Public route if (!to.meta.requiresAuth) { return next() } // Check authentication if (!auth.isAuthenticated) { return next({ path: '/login', query: { redirect: to.fullPath } }) } // Check roles if specified if (to.meta.roles && !to.meta.roles.includes(auth.user.role)) { return next('/unauthorized') } next() }) // Component-level guard export default { beforeRouteEnter(to, from, next) { // No access to this here next(vm => { // Access component instance via vm vm.loadData() }) }, beforeRouteLeave(to, from, next) { // Confirm before leaving if form modified if (this.hasUnsavedChanges) { const answer = confirm('Leave without saving?') next(answer) } else { next() } } } ``` ### 14. ส่ง props ให้ route อย่างไร Vue Router ช่วยให้แยกคอมโพเนนต์ออกจากพารามิเตอร์ของ route ได้ ```javascript // Route configuration with props const routes = [ { path: '/user/:id', component: UserProfile, // Boolean mode: passes params as props props: true }, { path: '/search', component: SearchResults, // Function mode: custom transformation props: (route) => ({ query: route.query.q, page: parseInt(route.query.page) || 1, filters: route.query.filters?.split(',') || [] }) }, { path: '/static', component: StaticPage, // Object mode: static props props: { sidebar: true, theme: 'dark' } } ] ``` ```javascript // UserProfile.vue // SearchResults.vue ``` ## คำถามเกี่ยวกับ Pinia และการจัดการ state ### 15. Pinia ต่างจาก Vuex อย่างไร Pinia คือเครื่องมือจัดการ state อย่างเป็นทางการของ Vue 3 และมาแทน Vuex ด้วย API ที่กระชับ | คุณสมบัติ | Vuex | Pinia | |---------|------|-------| | Mutation | ต้องมี | ไม่ต้องการ | | โมดูล | ตั้งค่าซับซ้อน | store อิสระ | | TypeScript | รองรับจำกัด | รองรับเต็มในตัว | | API | Options | Composition + Options | | DevTools | รองรับ | รองรับเต็มที่ | ```javascript // Pinia Store with Composition API import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const useCartStore = defineStore('cart', () => { // State const items = ref([]) const discountCode = ref(null) // Getters (computed) const totalItems = computed(() => items.value.reduce((sum, item) => sum + item.quantity, 0) ) const totalPrice = computed(() => { const subtotal = items.value.reduce( (sum, item) => sum + item.price * item.quantity, 0 ) return discountCode.value ? subtotal * 0.9 : subtotal }) // Actions (direct functions) function addItem(product) { const existing = items.value.find(i => i.id === product.id) if (existing) { existing.quantity++ } else { items.value.push({ ...product, quantity: 1 }) } } function removeItem(productId) { const index = items.value.findIndex(i => i.id === productId) if (index > -1) { items.value.splice(index, 1) } } async function checkout() { const response = await api.createOrder(items.value) items.value = [] return response } return { items, discountCode, totalItems, totalPrice, addItem, removeItem, checkout } }) ``` ### 16. ทำให้ state ของ Pinia store คงอยู่ระหว่างเซสชันได้อย่างไร การ persist ช่วยรักษา state ไว้ระหว่างเซสชันของผู้ใช้ ```javascript // plugins/piniaPersistedState.js import { watch } from 'vue' export function createPersistedState(options = {}) { const { key = 'pinia', storage = localStorage, paths = null } = options return ({ store }) => { // Restore state on startup const savedState = storage.getItem(`${key}-${store.$id}`) if (savedState) { store.$patch(JSON.parse(savedState)) } // Persist changes watch( () => store.$state, (state) => { const toSave = paths ? paths.reduce((acc, path) => { acc[path] = state[path] return acc }, {}) : state storage.setItem( `${key}-${store.$id}`, JSON.stringify(toSave) ) }, { deep: true } ) } } // main.js import { createPinia } from 'pinia' import { createPersistedState } from './plugins/piniaPersistedState' const pinia = createPinia() pinia.use(createPersistedState({ key: 'app-state', paths: ['user', 'preferences'] // Persist only these keys })) ``` > **ข้อมูลอ่อนไหว** > > ควรหลีกเลี่ยงการเก็บข้อมูลอ่อนไหว (token, รหัสผ่าน) ใน localStorage สำหรับ token การยืนยันตัวตน ควรใช้คุกกี้แบบ httpOnly จะปลอดภัยกว่า ## คำถามขั้นสูง ### 17. สร้างระบบ plugin ใน Vue ได้อย่างไร Plugin ใช้ขยาย Vue ด้วยฟีเจอร์ระดับ global ```javascript // plugins/analyticsPlugin.js export const AnalyticsPlugin = { install(app, options = {}) { const { trackingId, debug = false } = options // Global injection available in all components const analytics = { trackEvent(category, action, label) { if (debug) { console.log('Analytics:', { category, action, label }) } // Logic to send to analytics service window.gtag?.('event', action, { event_category: category, event_label: label }) }, trackPage(path) { window.gtag?.('config', trackingId, { page_path: path }) } } // Make available via inject app.provide('analytics', analytics) // Add global property (discouraged in Composition API) app.config.globalProperties.$analytics = analytics // Custom directive for click tracking app.directive('track', { mounted(el, binding) { el.addEventListener('click', () => { analytics.trackEvent('click', binding.value, el.textContent) }) } }) // Automatic route change tracking app.mixin({ mounted() { if (this.$route) { analytics.trackPage(this.$route.path) } } }) } } // main.js import { AnalyticsPlugin } from './plugins/analyticsPlugin' app.use(AnalyticsPlugin, { trackingId: 'UA-XXXXX-X', debug: import.meta.env.DEV }) ``` ### 18. อธิบาย render function และประโยชน์ Render function ให้ความควบคุมการ render อย่างเต็มที่ เหมาะกับคอมโพเนนต์ที่เปลี่ยนแปลงสูง ```javascript // components/DynamicHeading.js import { h } from 'vue' // Functional component with render function export const DynamicHeading = { props: { level: { type: Number, default: 1, validator: (v) => v >= 1 && v <= 6 } }, setup(props, { slots }) { // h() creates a vnode // Arguments: type, props, children return () => h( `h${props.level}`, { class: 'dynamic-heading' }, slots.default?.() ) } } // Component with complex conditional logic export const ConditionalWrapper = { props: ['condition', 'wrapper'], setup(props, { slots }) { return () => { if (props.condition) { return h(props.wrapper, null, slots.default?.()) } return slots.default?.() } } } // Usage Level 2 Title Conditional content ``` ### 19. ทดสอบคอมโพเนนต์ Vue ด้วย Vitest อย่างไร Unit test ใช้ตรวจสอบพฤติกรรมแบบแยกของคอมโพเนนต์ ```javascript // components/__tests__/Counter.spec.js import { describe, it, expect, vi } from 'vitest' import { mount } from '@vue/test-utils' import Counter from '../Counter.vue' describe('Counter', () => { it('displays the initial value', () => { const wrapper = mount(Counter, { props: { initialValue: 5 } }) expect(wrapper.text()).toContain('5') }) it('increments the value on click', async () => { const wrapper = mount(Counter) await wrapper.find('button.increment').trigger('click') expect(wrapper.text()).toContain('1') }) it('emits an event when changed', async () => { const wrapper = mount(Counter) await wrapper.find('button.increment').trigger('click') expect(wrapper.emitted('change')).toBeTruthy() expect(wrapper.emitted('change')[0]).toEqual([1]) }) it('calls the service on submit', async () => { const mockSubmit = vi.fn() const wrapper = mount(Counter, { global: { provide: { submitService: mockSubmit } } }) await wrapper.find('form').trigger('submit') expect(mockSubmit).toHaveBeenCalled() }) }) ``` ### 20. จัดการ error แบบ global ใน Vue ได้อย่างไร Vue 3 มีกลไกหลายอย่างสำหรับดักจับและจัดการ error ```javascript // main.js import { createApp } from 'vue' const app = createApp(App) // Global handler for component errors app.config.errorHandler = (err, instance, info) => { // err: the error // instance: the component instance // info: string describing where the error occurred console.error('Vue error:', err) console.error('Component:', instance?.$options?.name) console.error('Info:', info) // Send to monitoring service errorTracker.captureException(err, { component: instance?.$options?.name, info }) } // Handler for warnings (dev only) app.config.warnHandler = (msg, instance, trace) => { console.warn('Vue warning:', msg) } ``` ```javascript // ErrorBoundary component An error occurred {{ error.message }} Retry ``` ## คำถามเกี่ยวกับแนวทางที่ดี ### 21. ควรปฏิบัติตามกฎการตั้งชื่อใดใน Vue กฎการตั้งชื่อช่วยให้โค้ดอ่านและดูแลรักษาได้ง่ายขึ้น ```javascript // Component naming // PascalCase for files and names // BaseButton.vue, AppHeader.vue, TheNavbar.vue // Props: camelCase in JS, kebab-case in template defineProps<{ userName: string // JS isActive: boolean // JS }>() // // Events: camelCase with action prefix const emit = defineEmits<{ (e: 'updateValue', value: string): void // ✅ (e: 'submit'): void // ✅ (e: 'value-updated'): void // ❌ Avoid }>() // Composables: use prefix // useAuth.js, useFetch.js, useLocalStorage.js // Pinia stores: use prefix + Store suffix // useUserStore, useCartStore, useSettingsStore // Constants: SCREAMING_SNAKE_CASE const MAX_RETRY_COUNT = 3 const API_BASE_URL = '/api/v1' ``` ### 22. จัดโครงสร้างโปรเจกต์ Vue ขนาดใหญ่อย่างไร โครงสร้างแบบโมดูลช่วยให้นำทางและบำรุงรักษาง่ายขึ้น ``` src/ ├── assets/ # Static files ├── components/ │ ├── ui/ # Generic components (Button, Modal) │ └── common/ # Reusable business components ├── composables/ # Reusable logic │ ├── useAuth.js │ └── useFetch.js ├── layouts/ # Page layouts │ ├── DefaultLayout.vue │ └── AuthLayout.vue ├── modules/ # Functional modules │ ├── auth/ │ │ ├── components/ │ │ ├── composables/ │ │ ├── stores/ │ │ └── views/ │ └── dashboard/ ├── plugins/ # Vue plugins ├── router/ │ ├── index.js │ └── guards.js ├── stores/ # Global Pinia stores ├── types/ # TypeScript types ├── utils/ # Pure utilities └── views/ # Pages/Routes ``` ### 23. เมื่อใดควรใช้ v-if แทน v-show การเลือกระหว่าง `v-if` กับ `v-show` ขึ้นกับความถี่ของการสลับสถานะ ```javascript // v-if: low initial cost, expensive toggle // Removes/adds element from DOM // Ideal for: rarely modified conditions // v-show: higher initial cost, fast toggle // Uses display: none // Ideal for: frequent toggles Contextual information {{ content }} ``` ### 24. ปรับแต่งลิสต์ด้วย v-for อย่างไร การปรับแต่งลิสต์เป็นเรื่องสำคัญเมื่อมีจำนวนรายการมาก ```javascript // Always use :key with a unique stable identifier {{ item.name }} {{ item.name }} // Filtering and sorting: use computed // Virtualization for very long lists {{ item.data.name }} ``` ### 25. อธิบายแพตเทิร์น Renderless component คอมโพเนนต์แบบ renderless ห่อตรรกะไว้โดยไม่กำหนดโครงสร้าง HTML ```javascript // components/MouseTracker.vue ``` ```javascript // Usage: full control over rendering Position: {{ x }}, {{ y }} ``` แพตเทิร์นนี้แยกตรรกะออกจากการนำเสนออย่างสมบูรณ์ ทำให้นำกลับมาใช้ได้สูงสุด ## บทสรุป คำถามทั้ง 25 ข้อนี้ครอบคลุมแนวคิดสำคัญที่มักถูกประเมินในการสัมภาษณ์ Vue.js: - ✅ **Reactivity**: `ref`, `reactive`, `computed`, `watch` - ✅ **Composition API**: composable, `script setup`, TypeScript - ✅ **ประสิทธิภาพ**: lazy loading, virtualization, การปรับแต่ง - ✅ **Vue Router**: guard, props, การนำทาง - ✅ **Pinia**: store, persistence, action แบบอะซิงโครนัส - ✅ **แนวทางที่ดี**: โครงสร้าง ข้อตกลง แพตเทิร์นขั้นสูง การเตรียมตัวที่มีประสิทธิภาพควรผสานความเข้าใจเชิงทฤษฎีกับการลงมือเขียนโค้ด แต่ละแนวคิดที่กล่าวถึงในที่นี้สามารถนำไปสู่คำถามต่อยอดเชิงลึกในห้องสัมภาษณ์ได้ --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/vue-nuxt/essential-vuejs-interview-questions
{{ error.message }}