Vue 3 mang đến kiến trúc mới dựa trên Composition API, loại bỏ các tùy chọn như data, methods, computed hay watch ở cấp độ tùy chọn — thay vào đó, mọi logic đều được tổ chức trong hàm setup(). Đây là nền tảng để xây dựng ứng dụng linh hoạt, dễ kiểm thử và hỗ trợ mạnh mẽ cho TypeScript.
Tự động biên dịch TypeScript
Để kích hoạt giám sát và biên dịch tự động mã TypeScript trong dự án Vue 3:
tsc --watch --noEmit false
Hoặc cấu hình tsconfig.json với "composite": true và tích hợp vào script dev trong package.json.
Export trong module ES
Thay vì export default kiểu Options API, Vue 3 khuyến khích sử dụng export rõ ràng cho các hàm, hằng số hoặc composable:
export const useCounter = () => {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
};
Binding dữ liệu và thuộc tính
Sử dụng cú pháp {{ }}, v-bind (hoặc ký hiệu rút gọn :) và v-html:
<div :class="['box', isActive ? 'active' : '']">
<p v-html="rawHtml"></p>
<img :src="avatarUrl" :alt="user.name" />
</div>
Binding sự kiện và xử lý tương tác
Các trình xử lý sự kiện được gắn qua @click, @keyup.enter, v.v. Trong Composition API, chúng thường được định nghĩa trực tiếp trong setup():
const handleSubmit = () => {
if (formValid.value) {
api.submit(formData.value).then(() => {
notification.success('Gửi thành công');
});
}
};
Truy cập DOM với ref
Không dùng this.$refs như Vue 2, mà khai báo ref và gắn vào phần tử:
const inputRef = ref(null);
onMounted(() => {
inputRef.value?.focus();
});
<input ref="inputRef" type="text" />
Two-way binding với v-model
Với component tùy chỉnh, v-model được triển khai bằng cách phát sự kiện update:propName:
// Con
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
const updateValue = (val) => emit('update:modelValue', val);
<input
:value="props.modelValue"
@input="updateValue($event.target.value)"
/>
Props và truyền dữ liệu giữa component
Cha → con thông qua props, con → cha qua emit. Với nhiều giá trị, có thể dùng defineModel (Vue 3.4+):
const model = defineModel('checked');
Truyền dữ liệu không theo props (non-prop attributes) sẽ tự động lan xuống phần tử gốc — tắt bằng inheritAttrs: false và phân phối thủ công bằng v-bind="$attrs".
Slot và nội dung động
Slot mặc định, có tên và slot có phạm vi (scoped slot) được hỗ trợ đầy đủ:
<UserProfile>
<template #header>
<h2>Thông tin cá nhân</h2>
</template>
<template #default="{ user }">
<p>{{ user.email }}</p>
</template>
</UserProfile>
Composition API cốt lõi
ref(): tạo reactive primitivereactive(): tạo reactive objecttoRefs(): chuyển reactive object thành tậprefđộc lậpcomputed(): tính toán dựa trên dependencywatch()vàwatchEffect(): quan sát thay đổi, khác biệt ở cơ chế tracking tự độngprovide()/inject(): chia sẻ trạng thái giữa các tầng component
Chu kỳ sống và tối ưu hóa
Các hook như onMounted, onUnmounted, onBeforeUpdate thay thế mounted, beforeDestroy. Để đảm bảo DOM đã cập nhật sau thay đổi phản ứng:
await nextTick();
// DOM đã render xong
Giữ trạng thái component với <KeepAlive>
Giúp cache trạng thái component khi chuyển đổi (ví dụ: tab, route):
<KeepAlive include="UserProfile">
<RouterView />
</KeepAlive>
Routing với Vue Router 4
Cấu hình tuyến đường dạng composition-based:
const routes = [
{ path: '/user/:id', component: UserPage, props: true },
{ path: '/admin', name: 'admin', component: AdminPanel }
];
const router = createRouter({
history: createWebHistory(),
routes
});
Dùng useRouter() và useRoute() trong setup() để điều hướng và đọc tham số.
Quản lý trạng thái với Pinia (thay Vuex)
Pinia là giải pháp chính thức cho quản lý trạng thái trong Vue 3:
export const useAuthStore = defineStore('auth', {
state: () => ({ token: '', user: null }),
actions: {
async login(credentials) {
const res = await api.login(credentials);
this.token = res.token;
this.user = res.user;
}
}
});
Tích hợp TypeScript
Khai báo kiểu tường minh cho props, emits và store:
interface User {
id: number;
name: string;
email: string;
}
const props = defineProps<{
user: User;
editable?: boolean;
}>();
Yêu cầu HTTP và xử lý JSONP
Dùng axios cho REST API; với endpoint yêu cầu JSONP (ví dụ: API thời tiết cũ), kết hợp fetch-jsonp:
import jsonp from 'fetch-jsonp';
const fetchWeather = async (city) => {
const res = await jsonp(`https://api.example.com/weather?city=${city}`);
return res.json();
};
Teleport và quản lý vị trí DOM
Render nội dung ra ngoài cây DOM hiện tại — hữu ích cho modal, tooltip:
<Teleport to="#modal-root">
<Modal v-if="showModal" @close="showModal = false" />
</Teleport>
Mixins và tái sử dụng logic
Thay vì mixins (đã lỗi thời), nên dùng composable functions — ví dụ useDebounce:
export function useDebounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
Serverless và tích hợp backend
Vue frontend có thể kết nối với các hàm serverless (Vercel Functions, AWS Lambda) thông qua API routes — giảm phụ thuộc vào server truyền thống và tăng khả năng mở rộng.