📘 PHẦN 5: NHẬP MÔN NUXT.JS - VUE FRAMEWORK
🎯 Mục tiêu tổng quát
- Hiểu được Nuxt.js là gì và những lợi ích cốt lõi nó mang lại so với một ứng dụng Vue thông thường (SEO, hiệu năng, trải nghiệm lập trình).
- Làm quen với cấu trúc thư mục đặc biệt của Nuxt và cách nó tự động hóa nhiều công việc.
- Nắm vững cơ chế routing dựa trên file và cách tạo các layout dùng chung.
- Sử dụng composable
useFetchtích hợp sẵn của Nuxt để lấy dữ liệu một cách hiệu quả trên cả server và client. - Di chuyển thành công một ứng dụng Vue đã có sang cấu trúc Nuxt.js.
🧑🏫 Bài 1: Tại sao lại là Nuxt.js?
Nuxt.js là gì?
Nuxt.js là một framework mã nguồn mở, miễn phí, xây dựng trên nền tảng Vue.js. Nó không thay thế Vue, mà bổ sung các quy ước và công cụ mạnh mẽ để bạn có thể xây dựng các ứng dụng Vue hiện đại, hiệu suất cao và sẵn sàng cho production một cách nhanh chóng.
Các vấn đề Vue SPA gặp phải và giải pháp của Nuxt
Một ứng dụng Vue SPA (tạo bằng Vite) render ở phía client (CSR) và có các nhược điểm tương tự React SPA:
- SEO kém: Các công cụ tìm kiếm khó đọc nội dung vì ban đầu chỉ nhận được một file HTML gần như trống.
- Thời gian tải ban đầu chậm: Người dùng phải tải và chạy JavaScript trước khi thấy bất kỳ nội dung nào.
Nuxt giải quyết các vấn đề này bằng cách cho phép bạn render ứng dụng trên server trước khi gửi về cho client.
Các chế độ Rendering
Nuxt.js cực kỳ linh hoạt và hỗ trợ nhiều chiến lược rendering khác nhau:
- Universal Rendering (SSR - Server-Side Rendering): Chế độ mặc định. HTML được tạo ra trên server cho mỗi request. Tốt cho SEO và Time-to-Content.
- Static Site Generation (SSG): Toàn bộ website được render thành các file HTML tĩnh tại thời điểm build. Cực kỳ nhanh và an toàn.
- Client-Side Rendering (CSR): Hoạt động giống như một SPA Vue thông thường (nếu bạn muốn).
- Hybrid Rendering: Kết hợp các chế độ trên cho từng route khác nhau.
🧑🏫 Bài 2: Thiết lập và cấu trúc dự án Nuxt.js
Khởi tạo dự án
Nuxt cung cấp một công cụ CLI để tạo dự án.
npx nuxi@latest init simplestore-nuxtLệnh này sẽ tạo một dự án Nuxt tối giản. Sau đó, di chuyển vào thư mục và cài đặt dependencies:
cd simplestore-nuxt
npm install
npm run devCấu trúc thư mục "thần kỳ" của Nuxt
Nuxt sử dụng một cấu trúc thư mục dựa trên quy ước. Nếu bạn đặt file vào đúng thư mục, Nuxt sẽ tự động "hiểu" và cấu hình mọi thứ cho bạn.
app.vue: File gốc của ứng dụng. Tương tựApp.vuetrong dự án Vue thông thường. Nó thường chứa component<NuxtPage />(tương tự<router-view>).pages/: Thư mục quan trọng nhất. Chứa các trang và tự động tạo ra cấu hình Vue Router.components/: Chứa các component Vue. Các component trong này sẽ được tự động import.layouts/: Chứa các layout dùng chung cho các trang.composables/: Chứa các composable functions. Các hàm trong này cũng được tự động import.server/: Chứa code chạy trên server, ví dụ như các API route.assets/: Chứa các tài sản cần được xử lý bởi bundler (CSS, SASS, images...).public/: Chứa các tài sản tĩnh không cần xử lý (favicon.ico, robots.txt).
🧑🏫 Bài 3: File-based Routing và Layouts
Routing dựa trên thư mục pages/
Bạn không cần phải tạo file router/index.js thủ công nữa! Nuxt sẽ đọc cấu trúc thư mục pages/ và tự động tạo các route.
- Đầu tiên, tạo thư mục
pages/trong project của bạn. - Tạo các file
.vuebên trong:
pages/index.vue->http://localhost:3000/pages/about.vue->http://localhost:3000/aboutpages/cart.vue->http://localhost:3000/cart
Sau đó, trong app.vue, bạn chỉ cần thêm component <NuxtPage />:
<!-- app.vue -->
<template>
<div>
<!-- Có thể thêm Header và Footer ở đây -->
<NuxtPage /> <!-- Component trang sẽ được render ở đây -->
</div>
</template>Dynamic Routes
Để tạo route động, sử dụng dấu ngoặc vuông [] trong tên file hoặc thư mục.
pages/products/[id].vue-> sẽ khớp với/products/1,/products/abc, ...Trong component này, bạn có thể truy cập
idbằng composableuseRoute():vue<script setup> const route = useRoute(); const productId = route.params.id; </script>
Layouts lồng nhau
Layouts cho phép bạn có một giao diện chung cho một nhóm các trang.
Tạo file layout:
layouts/default.vuevue<!-- layouts/default.vue --> <template> <div> <AppHeader /> <main> <slot /> <!-- Trang con sẽ được render vào đây --> </main> <AppFooter /> </div> </template>default.vuesẽ tự động được áp dụng cho tất cả các trang.Tạo layout tùy chỉnh:
layouts/dashboard.vuevue<!-- layouts/dashboard.vue --> <template> <div class="dashboard-layout"> <DashboardSidebar /> <slot /> </div> </template>Để sử dụng layout này, trong một trang cụ thể, bạn dùng
<NuxtLayout>component hoặc macrodefinePageMeta:vue<!-- pages/admin/index.vue --> <script setup> definePageMeta({ layout: 'dashboard' }); </script>
🧑🏫 Bài 4: Auto-imports và Data Fetching với useFetch
Tự động import (Auto-imports)
Đây là một trong những tính năng giúp tăng tốc độ phát triển nhất của Nuxt.
- Mọi component trong
components/đều có thể được sử dụng trong các trang và component khác mà không cần import thủ công. - Mọi composable trong
composables/cũng được tự động import. - Các hàm tiện ích của Nuxt (như
useFetch,useRoute,useState) cũng có sẵn ở mọi nơi.
Composable useFetch của Nuxt
Nuxt cung cấp một composable useFetch được tối ưu hóa, mạnh mẽ hơn fetch thông thường.
- Chạy trên cả server và client: Khi người dùng truy cập trang lần đầu,
useFetchchạy trên server để render HTML. Khi người dùng điều hướng trên client, nó chạy trên client. - Xử lý trạng thái: Tự động quản lý các state
pending(loading),error,data. - Ngăn chặn request trùng lặp: Có cơ chế cache thông minh.
Ví dụ:
<!-- pages/index.vue -->
<script setup>
const { data: products, pending, error } = await useFetch('https://fakestoreapi.com/products');
// `await` ở top-level của <script setup> được hỗ trợ trong Nuxt
// Nó sẽ "block" việc render phía server cho đến khi data fetch xong
</script>
<template>
<div>
<h1>Sản phẩm</h1>
<div v-if="pending">Đang tải...</div>
<div v-else-if="error">Lỗi: {{ error.message }}</div>
<ul v-else>
<li v-for="product in products" :key="product.id">
{{ product.title }}
</li>
</ul>
</div>
</template>🧪 BÀI TẬP LỚN CUỐI PHẦN: Di chuyển "SimpleStore" sang Nuxt.js
Mô tả bài toán
Xây dựng lại dự án "SimpleStore" từ đầu bằng Nuxt.js, tận dụng các tính năng tự động hóa và tối ưu hóa của framework.
Yêu cầu
- Thiết lập dự án: Tạo một dự án Nuxt mới tên là
simplestore-nuxt. - Di chuyển Components:
- Copy các component Vue cũ (
ProductCard.vue,AppHeader.vue...) vào thư mụccomponents/của dự án mới. - Nhờ có auto-import, bạn có thể xóa tất cả các câu lệnh
importcomponent trong các file.
- Copy các component Vue cũ (
- Xây dựng Routing và Layout:
- Xóa file
app.vuenếu bạn muốn dùng layout. - Tạo một layout mặc định trong
layouts/default.vue, đặtAppHeadervàAppFooter(nếu có) vào đó, và một<slot />ở giữa. - Tạo các file trang trong thư mục
pages/:pages/index.vue: Trang chủ.pages/cart.vue: Trang giỏ hàng.pages/products/[id].vue: Trang chi tiết sản phẩm.
- Xóa file
- Lấy dữ liệu kiểu Nuxt:
- Trong
pages/index.vue, sử dụnguseFetchđể lấy và hiển thị danh sách sản phẩm. - Trong
pages/products/[id].vue, sử dụnguseRouteđể lấyidvà sau đó dùnguseFetchđể lấy dữ liệu chi tiết cho sản phẩm đó.
- Trong
- Quản lý State với Pinia:
- Nuxt tích hợp sẵn với Pinia. Cài đặt module:
npm install @pinia/nuxt. - Thêm
@pinia/nuxtvàomodulestrong filenuxt.config.ts. - Tạo store của bạn trong thư mục
stores/. Ví dụ:stores/cart.js. Store này sẽ được tự động đăng ký. - Sử dụng
useCartStoretrong các component mà không cần import thủ công.
- Nuxt tích hợp sẵn với Pinia. Cài đặt module:
Mục tiêu: Kết thúc phần này, bạn sẽ có một phiên bản "SimpleStore" chạy trên Nuxt.js, được hưởng lợi từ Server-Side Rendering (tốt cho SEO), code gọn gàng hơn nhờ auto-imports, và logic fetch dữ liệu mạnh mẽ hơn.
