Giao diện
Chế độ render
Mặc định của framework: Nitro chạy trên server, giao diện render ở trình duyệt. Nghe mâu thuẫn với "Nuxt SSR" nên trang này nói rõ vì sao và khi nào phá lệ.
SSR không phải BFF
Hai thứ hay bị gộp làm một:
| Chạy ở đâu | Framework dùng để làm gì | |
|---|---|---|
| Nitro / BFF | Server | Proxy /api/**, giữ cookie phiên httpOnly, gọi IAM. Luôn bật |
| SSR (render component) | Server | Sinh HTML sẵn cho trang. Mặc định tắt |
Cấu hình trong layer:
ts
ssr: true, // giữ Nitro + BFF
routeRules: { '/**': { ssr: false } }, // nhưng trang thì render ở clientĐừng đặt ssr: false ở cấp nuxt.config. Làm vậy là bỏ hẳn khả năng render phía server: không route nào bật lại được nữa (routeRules: { ssr: true } mất tác dụng), và nếu triển khai dạng tĩnh (nuxi generate + hosting tĩnh) thì Nitro không chạy — /api/**, /auth/** và chỗ trú của token biến mất cùng. Giữ ssr: true, điều chỉnh bằng routeRules.
Vì sao mặc định CSR
Ant Design Vue 4 dùng cssinjs, render phía server còn nhiều điểm gãy:
- FOUC: style chèn sau khi HTML đã hiện, trang nháy một nhịp không có định dạng.
- Hydration mismatch: component dùng Teleport (Modal, Drawer, Dropdown, Tooltip) và các nhánh responsive cho kết quả khác nhau giữa server và client.
message/notificationgọi theo kiểu lệnh, khởi tạo ngoài cây component nên mất theme khi chạy trên server.
Đổi lại, app nội bộ sau đăng nhập không cần SEO và người dùng vào bằng link trực tiếp rất ít. CSR là lựa chọn đơn giản và ổn định nhất cho phần lớn màn hình.
Điều này ảnh hưởng gì tới cách viết trang
useAsyncData/useFetchvẫn dùng bình thường, chỉ là chúng chạy ở client.- Đừng trông chờ HTML có sẵn nội dung: luôn xử lý trạng thái đang tải (
CTable :loading, skeleton,CEmpty). import.meta.servergần như luônfalsetrong code trang; logic chỉ chạy được trên server thì đưa vàoserver/(route Nitro).window,localStoragedùng được trongonMountedmà không cần bọc<ClientOnly>— nhưng vẫn nên tránh dùng ở top-level<script setup>để trang còn bật SSR được sau này.
Bật SSR cho một route
Trang công khai cần SEO hoặc first-paint nhanh (landing, trang tra cứu công khai, link chia sẻ):
ts
// nuxt.config.ts của dự án
export default defineNuxtConfig({
extends: ['@tasco/nuxt-layer-base'],
routeRules: {
'/landing': { ssr: true },
'/tra-cuu/**': { ssr: true },
},
})routeRules merge sâu nên chỉ cần khai route muốn đổi; phần '/**': { ssr: false } của layer vẫn giữ cho những route còn lại.
Route bật SSR phải tự né các điểm đau của antdv:
vue
<template>
<!-- Nội dung tĩnh: render được trên server, tốt cho SEO -->
<CPageHeader title="Tra cứu đơn hàng" />
<!-- Phần dùng Teleport/responsive: để client render -->
<ClientOnly>
<CTable :columns="columns" :data-source="dataSource" />
</ClientOnly>
</template>Và dùng App.useApp() của antdv thay cho message/notification gọi trực tiếp, để chúng lấy được theme từ context.
Kiểm tra thật trước khi bật
Bật SSR cho một route là quyết định của dự án, không có sẵn bảo chứng từ core. Kiểm tra: tắt JavaScript xem HTML có đủ nội dung không, mở console tìm cảnh báo hydration, và xem trang có nháy style lúc tải.
Các tuỳ chọn routeRules khác
| Rule | Tác dụng | Khi nào dùng |
|---|---|---|
{ ssr: false } | Trang render ở client | Mặc định của layer |
{ ssr: true } | Render HTML trên server | Trang công khai cần SEO |
{ prerender: true } | Sinh HTML tĩnh lúc build | Trang nội dung không đổi theo người dùng |
{ headers: { 'cache-control': '...' } } | Đặt header cho response | Tài nguyên tĩnh của dự án |
Không đặt redirect hay proxy trong routeRules cho /api/**: phần đó đã do BFF của layer xử lý, thêm rule sẽ vượt mặt và làm mất token. Xem BFF.
Liên quan
- Layer cung cấp gì — toàn bộ mặc định layer đặt sẵn.
- BFF: /api/** đi đâu — phần server luôn bật.
- Kiến trúc — bức tranh tổng thể.