Pertanyaan wawancara Vue.js: 25 pertanyaan untuk meraih pekerjaan
Persiapkan wawancara Vue.js dengan 25 pertanyaan penting ini. Dari reaktivitas hingga composables, kuasai konsep utama untuk wawancara berikutnya.

Wawancara Vue.js menilai jauh lebih banyak hal daripada sekadar sintaks framework. Recruiter ingin mengetahui penguasaan sistem reaktivitas, kemampuan menata kode dengan Composition API, serta keterampilan menyelesaikan masalah performa dan arsitektur yang nyata.
Setiap pertanyaan disertai jawaban rinci dan contoh kode. Untuk wawancara teknis, ada baiknya berlatih menjelaskan konsep dengan suara lantang seolah sedang menghadapi wawancara sungguhan.
Pertanyaan dasar Vue.js
1. Apa perbedaan antara ref dan reactive di Vue 3?
Pertanyaan ini menguji pemahaman tentang sistem reaktivitas, salah satu pondasi Vue 3. Perbedaan utamanya terletak pada tipe data yang ditangani dan sintaks aksesnya.
ref membuat referensi reaktif untuk nilai primitif (string, number, boolean) dan memerlukan .value untuk mengakses nilainya di dalam script. reactive membuat proxy reaktif untuk objek dan memberikan akses langsung ke propertinya.
// Contoh perbandingan ref vs 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 }) // ✅ CorrectAturan umum: gunakan ref untuk nilai sederhana dan reactive untuk objek terstruktur dengan beberapa properti yang saling terkait.
2. Bagaimana cara kerja sistem reaktivitas Vue 3?
Vue 3 memanfaatkan Proxy JavaScript (ES6) untuk mencegat operasi pada objek reaktif. Berbeda dengan Vue 2 yang memakai Object.defineProperty, pendekatan ini secara dinamis mendeteksi penambahan dan penghapusan properti.
// 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)Keunggulan Proxy meliputi: deteksi properti baru, dukungan Map dan Set, serta performa yang lebih baik pada objek besar.
3. Jelaskan perbedaan antara computed dan watch
computed dan watch melayani kebutuhan yang berbeda dalam pengelolaan reaktivitas.
Computed: menghitung nilai turunan dari data reaktif lain. Nilainya disimpan dalam cache dan hanya dihitung ulang saat dependensi berubah. Cocok untuk transformasi data.
Watch: menjalankan side effect sebagai respons terhadap perubahan. Berguna untuk pemanggilan API, interaksi dengan DOM, atau operasi asinkron.
// 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. Apa itu Virtual DOM dan bagaimana Vue memanfaatkannya?
Virtual DOM adalah representasi ringan dari DOM nyata dalam JavaScript. Vue menyimpan pohon virtual di memori dan menghitung perbedaan (diffing) antara state lama dan baru, sehingga DOM nyata hanya diperbarui pada bagian yang memang berubah.
// 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 elementsOptimasi Vue 3 mencakup: hoisting node statis, patch flag untuk mengidentifikasi tipe perubahan, serta tree-shaking pada compiler.
5. Bagaimana mengelola komunikasi antar komponen yang tidak punya relasi langsung?
Ada beberapa pola untuk komunikasi antar komponen yang tidak memiliki hubungan parent-child secara langsung.
// 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 }
}// 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')Untuk aplikasi yang lebih kompleks, Pinia tetap menjadi solusi yang direkomendasikan untuk pengelolaan state global.
Pertanyaan tentang Composition API
6. Apa keunggulan Composition API dibanding Options API?
Composition API memberikan beberapa keunggulan struktural dibanding Options API klasik.
Pengelompokan berdasarkan fitur: kode yang berkaitan dengan fitur yang sama berada dalam satu blok, sementara Options API memisahkannya berdasarkan tipe (data, methods, computed).
Penggunaan ulang lewat composables: logika dapat dipindahkan dengan mudah ke fungsi yang dapat digunakan kembali.
Dukungan TypeScript yang lebih baik: inferensi tipe alami tanpa decorator.
// 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. Bagaimana cara membuat composable yang dapat digunakan ulang?
Composable adalah fungsi yang membungkus logika reaktif. Konvensinya: memakai prefix use, mengembalikan objek yang berisi state dan method, serta menangani cleanup.
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 memakai konvensi useXxx untuk menegaskan sifatnya yang dapat digunakan ulang. Konvensi ini meningkatkan keterbacaan dan memudahkan mengenali dependensi reaktif.
8. Jelaskan watchEffect dibanding watch
watchEffect dan watch sama-sama merespons perubahan, namun pendekatannya berbeda.
watchEffect: berjalan langsung dan dijalankan ulang otomatis ketika dependensi reaktifnya berubah. Pelacakan dependensi dilakukan secara otomatis.
watch: mengamati sumber spesifik dan memberikan nilai lama serta baru. Memberikan kontrol lebih atas waktu pemicunya.
// 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. Bagaimana menangani props dengan TypeScript di script setup?
Sintaks <script setup> menyediakan integrasi TypeScript bawaan melalui defineProps dan withDefaults.
<script setup lang="ts">
// Interface for props
interface Props {
title: string
count?: number
items?: string[]
onSubmit?: (data: FormData) => void
}
// defineProps with generic typing
// withDefaults for default values
const props = withDefaults(defineProps<Props>(), {
count: 0,
items: () => [], // Factory for objects/arrays
onSubmit: undefined
})
// Props are automatically typed
console.log(props.title) // string
console.log(props.count) // number
// defineEmits with typing
const emit = defineEmits<{
(e: 'update', value: number): void
(e: 'delete', id: string): void
}>()
// Typed emit usage
const handleUpdate = () => {
emit('update', props.count + 1)
}
</script>Siap menguasai wawancara Vue.js / Nuxt.js Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pertanyaan performa
10. Teknik optimasi performa apa saja yang tersedia?
Vue 3 menyediakan beragam mekanisme untuk mengoptimalkan performa.
<template>
<div v-once>
<!-- This content will never be re-rendered -->
<ComplexStaticComponent />
</div>
</template>
// 2. v-memo: conditional memoization
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
<!-- Re-renders only if id or selected changes -->
{{ item.name }}
</div>
</template>
// 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-renderimport { 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
<template>
<KeepAlive :include="['Dashboard', 'UserProfile']" :max="10">
<component :is="currentView" />
</KeepAlive>
</template>11. Bagaimana mencegah re-render yang tidak perlu?
Re-render yang tidak perlu menurunkan performa. Ada beberapa strategi yang dapat menekan dampaknya.
// Problem: function created on each render
<template>
<!-- ❌ New function on every render -->
<ChildComponent @click="() => handleClick(item.id)" />
</template>
// Solution: use a method or ref
<script setup>
const handleItemClick = (id) => {
// Processing logic
}
</script>
<template>
<!-- ✅ Stable reference -->
<ChildComponent @click="handleItemClick(item.id)" />
</template>// 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. Jelaskan lazy loading komponen dan rute
Lazy loading memungkinkan kode dimuat sesuai permintaan, memperkecil ukuran bundle awal.
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 routerPertanyaan tentang Vue Router
13. Bagaimana mengamankan rute dengan guard?
Navigation guard digunakan untuk mengontrol akses ke setiap rute.
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. Bagaimana cara meneruskan props ke rute?
Vue Router memungkinkan komponen dipisahkan dari parameter rutenya.
// 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' }
}
]<script setup>
// Props are automatically injected
defineProps<{
id: string
}>()
</script>
// SearchResults.vue
<script setup>
defineProps<{
query: string
page: number
filters: string[]
}>()
</script>Pertanyaan tentang Pinia dan manajemen state
15. Apa perbedaan antara Pinia dan Vuex?
Pinia adalah state manager resmi untuk Vue 3 yang menggantikan Vuex dengan API yang lebih sederhana.
| Fitur | Vuex | Pinia | |---------|------|-------| | Mutation | Wajib | Tidak diperlukan | | Modul | Konfigurasi rumit | Store independen | | TypeScript | Dukungan terbatas | Native dan lengkap | | API | Options | Composition + Options | | DevTools | Mendukung | Dukungan penuh |
// 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. Bagaimana cara menjaga state Pinia tetap persisten?
Persistensi memungkinkan state tetap ada di antara sesi pengguna.
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
}))Sebaiknya hindari menyimpan data sensitif (token, kata sandi) di localStorage. Untuk token autentikasi, cookie httpOnly merupakan pilihan yang lebih aman.
Pertanyaan tingkat lanjut
17. Bagaimana cara merancang sistem plugin di Vue?
Plugin memungkinkan Vue diperluas dengan fitur global.
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. Jelaskan render function dan kegunaannya
Render function memberikan kontrol penuh atas proses render, sangat berguna untuk komponen yang sangat dinamis.
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
<DynamicHeading :level="2">Level 2 Title</DynamicHeading>
<ConditionalWrapper :condition="isLink" wrapper="a">
Conditional content
</ConditionalWrapper>19. Bagaimana cara menguji komponen Vue dengan Vitest?
Unit test memvalidasi perilaku komponen secara terisolasi.
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. Bagaimana menangani error secara global di Vue?
Vue 3 menyediakan beberapa mekanisme untuk menangkap dan menangani error.
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)
}// ErrorBoundary component
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
// Captures errors from child components
onErrorCaptured((err, instance, info) => {
error.value = {
message: err.message,
component: instance?.$options?.name,
info
}
// Return false to stop propagation
return false
})
const retry = () => {
error.value = null
}
</script>
<template>
<div v-if="error" class="error-boundary">
<h2>An error occurred</h2>
<p>{{ error.message }}</p>
<button @click="retry">Retry</button>
</div>
<slot v-else />
</template>Siap menguasai wawancara Vue.js / Nuxt.js Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pertanyaan tentang praktik terbaik
21. Konvensi penamaan apa yang perlu diikuti di Vue?
Konvensi penamaan meningkatkan keterbacaan dan kemudahan perawatan kode.
// 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
}>()
// <UserCard :user-name="name" :is-active="active" />
// 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. Bagaimana menyusun proyek Vue berskala besar?
Struktur modular memudahkan navigasi dan perawatan.
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/Routes23. Kapan memilih v-if dibanding v-show?
Pemilihan antara v-if dan v-show tergantung pada seberapa sering kondisi diubah.
// v-if: low initial cost, expensive toggle
// Removes/adds element from DOM
// Ideal for: rarely modified conditions
<template>
<!-- v-if: component not created if not admin -->
<AdminPanel v-if="user.isAdmin" />
<!-- v-if with v-else-if for multiple conditions -->
<LoadingSpinner v-if="isLoading" />
<ErrorMessage v-else-if="error" :message="error" />
<DataDisplay v-else :data="data" />
</template>
// v-show: higher initial cost, fast toggle
// Uses display: none
// Ideal for: frequent toggles
<template>
<!-- v-show: always rendered, frequent toggle -->
<Tooltip v-show="isHovered">
Contextual information
</Tooltip>
<!-- Accordion menu with frequent toggle -->
<div v-show="isExpanded" class="accordion-content">
{{ content }}
</div>
</template>24. Bagaimana mengoptimalkan list dengan v-for?
Optimalisasi list sangat penting untuk performa saat jumlah elemennya besar.
// Always use :key with a unique stable identifier
<template>
<!-- ✅ Unique and stable key -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
<!-- ❌ Index as key (reordering issues) -->
<li v-for="(item, index) in items" :key="index">
{{ item.name }}
</li>
</template>
// Filtering and sorting: use computed
<script setup>
import { computed } from 'vue'
const sortedAndFilteredItems = computed(() => {
return items.value
.filter(item => item.isActive)
.sort((a, b) => a.name.localeCompare(b.name))
})
</script>
// Virtualization for very long lists
<script setup>
import { useVirtualList } from '@vueuse/core'
const { list, containerProps, wrapperProps } = useVirtualList(
largeList,
{ itemHeight: 50 }
)
</script>
<template>
<div v-bind="containerProps" style="height: 400px; overflow: auto">
<div v-bind="wrapperProps">
<div v-for="item in list" :key="item.data.id">
{{ item.data.name }}
</div>
</div>
</div>
</template>25. Jelaskan pola Renderless component
Komponen renderless membungkus logika tanpa memaksakan struktur HTML.
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const x = ref(0)
const y = ref(0)
const updatePosition = (event) => {
x.value = event.clientX
y.value = event.clientY
}
onMounted(() => {
window.addEventListener('mousemove', updatePosition)
})
onUnmounted(() => {
window.removeEventListener('mousemove', updatePosition)
})
// Expose state via slot
defineExpose({ x, y })
</script>
<template>
<!-- Slot with props: parent decides the rendering -->
<slot :x="x" :y="y" />
</template>// Usage: full control over rendering
<template>
<MouseTracker v-slot="{ x, y }">
<div class="cursor-display">
Position: {{ x }}, {{ y }}
</div>
</MouseTracker>
<MouseTracker v-slot="{ x, y }">
<svg>
<circle :cx="x" :cy="y" r="10" fill="red" />
</svg>
</MouseTracker>
</template>Pola ini memisahkan logika sepenuhnya dari tampilan dan memaksimalkan reusability.
Kesimpulan
Kedua puluh lima pertanyaan ini mencakup konsep penting yang dievaluasi pada wawancara Vue.js:
- ✅ Reaktivitas:
ref,reactive,computed,watch - ✅ Composition API: composable,
script setup, TypeScript - ✅ Performa: lazy loading, virtualisasi, optimasi
- ✅ Vue Router: guard, props, navigasi
- ✅ Pinia: store, persistensi, action asinkron
- ✅ Praktik terbaik: struktur, konvensi, pola lanjutan
Persiapan yang efektif memadukan pemahaman teori dengan praktik koding. Setiap konsep yang dibahas di sini bisa menjadi pintu masuk pertanyaan yang lebih mendalam saat wawancara.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Vue 3 Composition API: Panduan Lengkap Menguasai Reaktivitas
Kuasai Vue 3 Composition API melalui panduan praktis ini. Pelajari ref, reactive, computed, watch, dan composables untuk membangun aplikasi Vue yang berkinerja tinggi.

Vue 3 Pinia vs Vuex: Panduan Lengkap State Management dan Pertanyaan Interview 2026
Perbandingan mendalam Pinia vs Vuex: arsitektur, TypeScript support, Composition API, performa, strategi migrasi, dan pertanyaan interview Vue state management 2026.

Nuxt 3: SSR dan generasi statis, panduan lengkap
Menguasai SSR dan generasi statis dengan Nuxt 3. Dari useFetch hingga route rules, pelajari cara mengoptimalkan performa aplikasi Vue.js.