# Câu hỏi phỏng vấn Vue.js: 25 câu để chinh phục công việc
> Chuẩn bị phỏng vấn Vue.js với 25 câu hỏi cốt lõi. Từ reactivity đến composables, làm chủ các khái niệm quan trọng cho buổi phỏng vấn tới.
- Published: 2026-02-05
- Updated: 2026-04-27
- Author: SharpSkill
- Tags: vue.js, interview, frontend, javascript, technical questions
- Reading time: 15 min
---
Phỏng vấn Vue.js đánh giá rất nhiều thứ vượt ra khỏi cú pháp framework. Nhà tuyển dụng muốn xác định mức độ thành thạo hệ thống reactivity, khả năng tổ chức mã với Composition API và năng lực giải quyết các vấn đề thực tế về hiệu năng cũng như kiến trúc.
> **Lời khuyên chuẩn bị**
>
> Mỗi câu hỏi đều có lời giải chi tiết và ví dụ mã. Trong các buổi phỏng vấn kỹ thuật, việc luyện tập trình bày to khái niệm như đang phỏng vấn thật sẽ rất hữu ích.
## Câu hỏi nền tảng về Vue.js
### 1. Sự khác nhau giữa ref và reactive trong Vue 3 là gì?
Câu hỏi này đánh giá hiểu biết về hệ thống reactivity, một trong những trụ cột của Vue 3. Khác biệt chính nằm ở loại dữ liệu được xử lý và cú pháp truy cập.
`ref` tạo ra một tham chiếu reactive cho các giá trị nguyên thủy (string, number, boolean) và đòi hỏi `.value` để truy cập giá trị trong script. `reactive` tạo ra một proxy reactive cho object và cho phép truy cập trực tiếp đến thuộc tính.
```javascript
// Ví dụ so sánh 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 }) // ✅ Correct
```
Quy tắc chung: dùng `ref` cho giá trị đơn giản và `reactive` cho object có nhiều thuộc tính liên quan.
### 2. Hệ thống reactivity của Vue 3 hoạt động ra sao?
Vue 3 dùng Proxy của JavaScript (ES6) để chặn các thao tác trên object reactive. Khác với Vue 2 vốn dựa vào `Object.defineProperty`, cách tiếp cận này phát hiện việc thêm hoặc xóa thuộc tính một cách động.
```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)
```
Ưu điểm của Proxy: phát hiện thuộc tính mới, hỗ trợ Map và Set, hiệu năng tốt hơn trên object lớn.
### 3. Hãy giải thích sự khác nhau giữa computed và watch
`computed` và `watch` phục vụ những nhu cầu khác nhau trong việc quản lý reactivity.
**Computed**: tính toán giá trị suy ra từ các dữ liệu reactive khác. Giá trị được lưu cache và chỉ tính lại khi dependency thay đổi. Phù hợp cho biến đổi dữ liệu.
**Watch**: thực thi side effect khi có thay đổi. Hữu ích cho gọi API, tương tác DOM hoặc các thao tác bất đồng bộ.
```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 là gì và Vue sử dụng nó thế nào?
Virtual DOM là biểu diễn nhẹ trong JavaScript của DOM thực. Vue duy trì một cây ảo trong bộ nhớ và tính toán chênh lệch (diffing) giữa trạng thái cũ và mới để áp dụng đúng những thay đổi cần thiết lên DOM thực.
```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
```
Các tối ưu của Vue 3 bao gồm: hoist node tĩnh, patch flag để xác định kiểu thay đổi và tree-shaking trong compiler.
### 5. Làm sao để xử lý giao tiếp giữa các component không có quan hệ trực tiếp?
Có nhiều mẫu để cho các component không có quan hệ cha-con trực tiếp giao tiếp với nhau.
```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')
```
Với những ứng dụng phức tạp hơn, Pinia vẫn là giải pháp được khuyên dùng để quản lý state toàn cục.
## Câu hỏi về Composition API
### 6. Composition API có ưu điểm gì so với Options API?
Composition API có nhiều ưu điểm cấu trúc so với Options API truyền thống.
**Tổ chức theo tính năng**: mã liên quan đến cùng một tính năng được nhóm lại, trong khi Options API tách theo loại (data, methods, computed).
**Tái sử dụng nhờ composables**: trích logic ra hàm dùng lại được dễ dàng.
**Hỗ trợ TypeScript tốt hơn**: suy luận kiểu tự nhiên, không cần 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. Làm thế nào để tạo một composable dùng lại được?
Composable là hàm bao gói logic reactive. Quy ước: tiền tố `use`, trả về object gồm state và method, đồng thời xử lý 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 })
```
> **Quy ước đặt tên**
>
> Composable tuân theo quy ước `useXxx` để thể hiện tính chất tái sử dụng. Quy ước này giúp mã dễ đọc và dễ nhận diện các phụ thuộc reactive.
### 8. Hãy giải thích watchEffect so với watch
`watchEffect` và `watch` đều phản ứng với thay đổi nhưng theo cách khác nhau.
**watchEffect**: chạy ngay lập tức và tự động chạy lại khi các phụ thuộc reactive thay đổi. Việc theo dõi phụ thuộc diễn ra tự động.
**watch**: quan sát các nguồn cụ thể và cung cấp cả giá trị cũ lẫn mới. Cho phép kiểm soát thời điểm kích hoạt nhiều hơn.
```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. Làm sao để xử lý props với TypeScript trong script setup?
Cú pháp `
```
## Câu hỏi về hiệu năng
### 10. Có những kỹ thuật tối ưu hiệu năng nào?
Vue 3 cung cấp nhiều cơ chế tối ưu hiệu năng.
```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. Làm sao tránh re-render không cần thiết?
Re-render không cần thiết ảnh hưởng đến hiệu năng. Có nhiều chiến lược giúp giảm thiểu chúng.
```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. Hãy giải thích lazy loading cho component và route
Lazy loading cho phép tải mã theo nhu cầu, giảm dung lượng bundle ban đầu.
```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
```
## Câu hỏi về Vue Router
### 13. Làm sao bảo vệ route bằng guard?
Navigation guard cho phép kiểm soát quyền truy cập 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. Làm thế nào truyền props vào route?
Vue Router cho phép tách rời component khỏi tham số 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
```
## Câu hỏi về Pinia và quản lý state
### 15. Pinia khác Vuex ở những điểm nào?
Pinia là trình quản lý state chính thức cho Vue 3, thay thế Vuex bằng API đơn giản hơn.
| Đặc điểm | Vuex | Pinia |
|---------|------|-------|
| Mutation | Bắt buộc | Không cần |
| Module | Cấu hình phức tạp | Store độc lập |
| TypeScript | Hỗ trợ giới hạn | Tích hợp đầy đủ |
| API | Options | Composition + Options |
| DevTools | Có hỗ trợ | Hỗ trợ đầy đủ |
```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. Làm sao để duy trì state của Pinia?
Persistence giúp giữ state giữa các phiên người dùng.
```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
}))
```
> **Dữ liệu nhạy cảm**
>
> Nên tránh lưu trữ dữ liệu nhạy cảm (token, mật khẩu) trong localStorage. Với token xác thực, cookie httpOnly sẽ an toàn hơn.
## Câu hỏi nâng cao
### 17. Làm thế nào triển khai hệ thống plugin trong Vue?
Plugin cho phép mở rộng Vue bằng các tính năng toàn cục.
```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. Hãy giải thích render function và công dụng của chúng
Render function cho phép kiểm soát hoàn toàn quá trình render, hữu ích với những component rất linh động.
```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. Làm sao kiểm thử component Vue với Vitest?
Unit test xác minh hành vi đơn lẻ của component.
```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. Làm sao xử lý lỗi toàn cục trong Vue?
Vue 3 cung cấp nhiều cơ chế để bắt và xử lý lỗi.
```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 }}
```
## Câu hỏi về thực hành tốt
### 21. Cần tuân thủ quy ước đặt tên nào trong Vue?
Quy ước đặt tên giúp mã dễ đọc và dễ bảo trì hơn.
```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. Làm sao tổ chức một dự án Vue lớn?
Cấu trúc theo module giúp việc duyệt và bảo trì dễ dàng.
```
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. Khi nào dùng v-if thay vì v-show?
Lựa chọn giữa `v-if` và `v-show` phụ thuộc vào tần suất bật/tắt.
```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. Làm sao tối ưu danh sách với v-for?
Tối ưu danh sách rất quan trọng khi số lượng phần tử lớn.
```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. Hãy giải thích pattern Renderless component
Component renderless bao gói logic mà không áp đặt cấu trúc HTML.
```javascript
// components/MouseTracker.vue
```
```javascript
// Usage: full control over rendering
Position: {{ x }}, {{ y }}
```
Pattern này tách hoàn toàn logic khỏi giao diện trình bày, tối đa hóa khả năng tái sử dụng.
## Kết luận
25 câu hỏi này bao quát các khái niệm cốt lõi được đánh giá trong phỏng vấn Vue.js:
- ✅ **Reactivity**: `ref`, `reactive`, `computed`, `watch`
- ✅ **Composition API**: composable, `script setup`, TypeScript
- ✅ **Hiệu năng**: lazy loading, virtual list, các tối ưu
- ✅ **Vue Router**: guard, props, điều hướng
- ✅ **Pinia**: store, persistence, action bất đồng bộ
- ✅ **Thực hành tốt**: cấu trúc, quy ước, pattern nâng cao
Việc chuẩn bị hiệu quả phải kết hợp giữa hiểu lý thuyết và thực hành mã. Mỗi khái niệm trên đều có thể trở thành điểm khởi đầu cho những câu hỏi sâu hơn trong buổi phỏng vấn.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/vi/blog/vue-nuxt/essential-vuejs-interview-questions