Skip to content

Thêm vào dự án có sẵn

Dùng khi đã có một app Nuxt và muốn chuyển sang nền tảng chung. Dự án mới thì đi đường Tạo app mới cho nhanh.

Điều kiện

Yêu cầuVì sao
Nuxt ^4.0.0, Vue ^3.5Layer khai nuxt/vue là peer dependency
Chưa extends layer nào khác, hoặc chỉ mộtQuy ước tối đa 2 tầng layer (base → app)
Chưa dùng đường dẫn /auth/**/api/** cho mục đích khácLayer chiếm hai tiền tố này cho BFF
Không dùng Pinia làm store chínhFramework dùng useState + cookie + app.config.ts

1. Registry và package

Thêm .npmrc như ở Chuẩn bị môi trường, rồi:

bash
pnpm add @tasco/nuxt-layer-base @tasco/ui

Ghim hai package cùng một phiên bản — cả bộ @tasco/* đi lockstep.

2. Khai báo layer

ts
// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@tasco/nuxt-layer-base'],
  compatibilityDate: '2025-01-01',
})

Layer mang theo: module @ant-design-vue/nuxt@nuxt/fonts, CSS nền, routeRules mặc định CSR, plugin UI/auth/permission/error, middleware auth.global + permission.global, layout mặc định, trang /auth/login, và toàn bộ route Nitro của BFF. Chi tiết: Layer cung cấp gì.

3. Gỡ cấu hình trùng

Xoá khỏi nuxt.config.ts của dự án những thứ layer đã lo — giữ lại sẽ chồng chéo hoặc ghi đè nhầm:

  • modules: ['@ant-design-vue/nuxt'] và mọi cấu hình antd.
  • CSS reset và import theme antdv thủ công.
  • ssr/routeRules tự đặt, trừ khi thực sự muốn ghi đè (xem Chế độ render).
  • Plugin tự đăng ký component antdv toàn cục.
  • Middleware tự kiểm tra đăng nhập — thay bằng definePageMeta({ auth, permissions }).

Đổi mọi import ... from 'ant-design-vue' sang @tasco/ui:

diff
- import { Table, Tag, Select } from 'ant-design-vue'
+ import { CSelect } from '@tasco/ui'

CTable, CTag, CButton, CForm, CCard, CPageHeader… đã đăng ký toàn cục nên bỏ hẳn import; primitive re-export (CSelect, CFormItem, CInput…) vẫn import từ @tasco/ui. ESLint của core sẽ báo lỗi nếu còn import trực tiếp ant-design-vue.

4. Biến môi trường

bash
# .env
NUXT_SESSION_SECRET=            # bắt buộc ở production: openssl rand -base64 32
NUXT_AUTH_BASE_URL=https://example.com/cop
NUXT_API_PROXY_TARGET=https://gateway.example.com

# Chưa cắm IAM thì chạy tạm:
# NUXT_AUTH_MOCK=true

Bảng đầy đủ: Cấu hình & biến môi trường.

5. Chuyển lời gọi backend sang BFF

Trước đây gọi thẳng gateway kèm token ở client; giờ gọi qua /api/** và để Nitro gắn token:

diff
- const data = await $fetch('https://gateway.example.com/orders', {
-   headers: { Authorization: `Bearer ${token}` },
- })
+ const api = useApi()
+ const data = await api('/orders')

Bỏ mọi chỗ lưu access token ở localStorage/sessionStorage/Pinia — token nằm trong cookie httpOnly, client không đọc được và cũng không cần đọc. Xem BFF.

6. Chạy và đối chiếu

bash
pnpm install
pnpm dev
  • [ ] Vào một trang bất kỳ → bị đưa về /auth/login (trừ trang khai auth: false).
  • [ ] Đăng nhập (mock hoặc IAM thật) → về đúng trang ban đầu, sidebar hiện menu từ app.config.ts.
  • [ ] Gọi một API qua useApi → tab Network thấy request tới /api/..., không thấy token.
  • [ ] pnpm lint && pnpm typecheck sạch.

Xung đột thường gặp

Tình huốngCách xử lý
Dự án đã có app/app.vueNuxt lấy bản của dự án. Phải tự bọc <a-config-provider> với theme + locale vi_VN và inject CSS vars, hoặc xoá file để dùng bản của layer
Dự án đã có app/layouts/default.vueBản của dự án thắng. Muốn giữ khung chung thì xoá, rồi khai báo menu qua app.config.ts
Đã có trang /auth/login riêngGiữ được, dùng lại <TascoLoginForm> bên trong; xem Form đăng nhập
Đã có server/api/** của riêng dự ánRoute cụ thể của dự án thắng catch-all của layer. Đặt tên khác /api/... nếu không muốn đi qua proxy
Trang cần SSR (SEO, chia sẻ link)Bật lại từng route: routeRules: { '/landing': { ssr: true } } và tự xử lý điểm đau của antdv — xem Chế độ render
Lỗi [TSCONFIG_ERROR] hoặc thiếu typeChạy nuxi prepare (hoặc pnpm typecheck) để sinh lại .nuxt/tsconfig.json

Tiếp theo

Hướng dẫn từng bước để dựng thử một màn hình theo đúng mẫu của framework.