Giao diện
Hiển thị lỗi
Framework có sẵn một điểm xử lý lỗi chung: phân loại, chọn kiểu hiển thị, chống lặp thông báo, và đưa về trang đăng nhập khi hết phiên. Trang này nói rõ nó làm gì tự động và khi nào bạn cần can thiệp.
Mặc định: không phải làm gì cả
Plugin 04.error của layer gắn sẵn:
errorHandlercủa Vue và hookvue:error— lưới an toàn cho lỗi chưa ai bắt.- Hàm
$tascoErrorđể trang gọi khi muốn tự quyết định chỗ bắt lỗi.
Quy tắc hiển thị:
| Loại lỗi | Người dùng thấy |
|---|---|
| 401 | Chuyển về /auth/login?redirect=… (không chuyển nếu đang ở trang đăng nhập) |
| 422 | Không có thông báo — form tự hiện lỗi từng field |
| 5xx, mất mạng | notification góc màn hình, tiêu đề "Lỗi hệ thống" |
| Còn lại (400, 403, 404…) | message ngắn giữa màn hình |
Thông báo giống hệt nhau trong vòng 3 giây chỉ hiện một lần — nhiều request cùng hỏng sẽ không phủ kín màn hình.
Tự bắt khi cần
ts
import { message } from '@tasco/ui'
const { $tascoError } = useNuxtApp()
async function remove(id: string) {
try {
await removeOrder(id)
message.success('Đã xoá đơn hàng.')
await reload()
} catch (caught) {
$tascoError(caught) // vẫn hiển thị theo quy tắc chung
}
}Dùng khi cần chạy thêm việc gì đó quanh lỗi (tắt loading, khôi phục trạng thái, ghi log), nhưng vẫn muốn giao diện báo lỗi thống nhất.
Tuỳ chọn
ts
$tascoError(caught, { silent: true }) // chỉ phân loại, không hiện gì
$tascoError(caught, { fallbackMessage: 'Không xoá được đơn hàng.' }) // khi lỗi không có message$tascoError trả về AppError đã chuẩn hoá nên dùng tiếp được:
ts
const error = $tascoError(caught, { silent: true })
if (error.status === 409) {
await confirmOverwrite()
}Tự dựng giao diện báo lỗi
Handler chung phù hợp cho thao tác ngắn (lưu, xoá). Với lỗi tải dữ liệu của cả màn hình, hiện trạng thái ngay trong trang thay vì bắn toast rồi để trang trống:
vue
<script setup lang="ts">
const { order, loading, error, load } = useOrderDetail(String(route.params.id))
</script>
<template>
<CCard v-if="loading">Đang tải…</CCard>
<CEmpty
v-else-if="error"
:description="error.message"
bordered
>
<CButton variant="primary" @click="load">Thử lại</CButton>
</CEmpty>
<CCard v-else>…</CCard>
</template>Trang lỗi toàn màn hình
app/error.vue của layer xử lý lỗi fatal (403 từ middleware quyền, 404 route không khớp, lỗi khi dựng app): hiện mã lỗi, thông báo và nút về trang chủ.
Dự án muốn giao diện riêng thì tạo app/error.vue của mình:
vue
<script setup lang="ts">
import type { NuxtError } from '#app'
const props = defineProps<{ error: NuxtError }>()
const isForbidden = computed(() => Number(props.error?.statusCode) === 403)
</script>
<template>
<div class="error-page">
<h1>{{ isForbidden ? 'Không có quyền truy cập' : 'Đã xảy ra lỗi' }}</h1>
<p>{{ error.message }}</p>
<CButton variant="primary" @click="clearError({ redirect: '/' })">Về trang chủ</CButton>
</div>
</template>Trong error.vue, lỗi là NuxtError nên mã nằm ở statusCode — khác với AppError ở phía client dùng status.
Lỗi khi render một trang
Layout mặc định bọc nội dung trang trong <NuxtErrorBoundary>: một trang lỗi lúc render chỉ làm vùng nội dung hiện thông báo, khung app và menu vẫn dùng được. Không cần tự bọc lại, trừ khi muốn khoanh vùng nhỏ hơn:
vue
<NuxtErrorBoundary>
<BieuDoDoanhThu :data="data" />
<template #error="{ error }">
<CEmpty :description="`Không vẽ được biểu đồ: ${error.message}`" bordered />
</template>
</NuxtErrorBoundary>Viết thông báo lỗi
- Tiếng Việt, đủ câu, nói việc gì hỏng và làm gì tiếp: "Không lưu được đơn hàng. Kiểm tra kết nối rồi thử lại."
- Không hiện mã kỹ thuật, tên hàm, chuỗi stack cho người dùng.
- Không bao giờ đưa token, header hay dữ liệu nhạy cảm vào thông báo.
- Thông báo từ backend (
statusMessage,message) được hiện thẳng — thống nhất với backend rằng những chuỗi đó viết cho người dùng cuối đọc.
Ghi log
Runtime không dùng console.log. Ở route Nitro, cần ghi thì dùng console.warn / console.error kèm tiền tố tên dự án, và không kèm dữ liệu nhạy cảm:
ts
console.error('[orders] Không gọi được dịch vụ tồn kho:', error.message)Liên quan
- Xử lý lỗi —
AppErrorvà cách phân loại. - Lỗi 422 và form — trường hợp cố tình không hiện toast.
- Chặn theo quyền — nguồn của lỗi 403.