Xây Dựng Website Tĩnh Với Next.js App Router, Fusionable và Markdown
Trong hướng dẫn chi tiết này, chúng ta sẽ khám phá cách tạo một website tĩnh sử dụng Next.js App Router (được giới thiệu trong Next.js 13) và Fusionable, với nội dung được quản lý qua Markdown. App Router không chỉ cung cấp một cấu trúc hiện đại cho ứng dụng React mà còn giúp tổ chức layouts, server components và việc lấy dữ liệu một cách hợp lý.
Các Công Cụ và Tính Năng Chúng Ta Sẽ Sử Dụng
Để xây dựng một website mẫu đơn giản, chúng ta sẽ cần sử dụng các công cụ và thư viện sau:
- Next.js: Đây là một framework mạnh mẽ dành cho React giúp bạn xây dựng các ứng dụng web một cách nhanh chóng và dễ dàng. Nó cung cấp nhiều tính năng như server-side rendering (SSR), static site generation (SSG) và API routes, biến nó thành sự lựa chọn lý tưởng cho phát triển ứng dụng web hiện đại.
- App Router: Tính năng này trong Next.js giúp bạn tổ chức ứng dụng của mình một cách logic và tiên tiến, tận dụng React Server Components (RSC) để tối ưu hóa hiệu suất và giảm thiểu JavaScript cho client.
- Fusionable: Đây là thư viện JavaScript mạnh mẽ cho phép bạn quản lý và truy vấn nội dung một cách dễ dàng. Nó hỗ trợ lọc, sắp xếp và truy vấn các dữ liệu có cấu trúc, rất thuận lợi cho những website tĩnh dựa trên Markdown.
- Showdown: Thư viện này giúp chuyển đổi nội dung Markdown thành HTML, rất hữu ích để hiển thị nội dung phong phú trên website tĩnh của bạn.
Thiết Lập Dự Án Next.js
Bắt đầu bằng việc tạo một dự án Next.js mới và cài đặt Fusionable cũng như Showdown:
bash
# Tạo ứng dụng Next.js mới với TypeScript
npx create-next-app@14 my-nextjs-site --typescript
# Cài đặt các thư viện cần thiết
cd my-nextjs-site
npm install fusionable showdown
Tùy Chọn Khi Tạo Ứng Dụng
Trong hướng dẫn này, chúng ta sẽ giữ cho ứng dụng thật tối giản với những tùy chọn cụ thể như sau:
- Không sử dụng ESLint
- Không sử dụng Tailwind CSS
- Sử dụng thư mục
src/
- Sử dụng App Router
- Không tùy chỉnh alias import mặc định (@/*)
Tổ Chức Nội Dung Markdown
Trước tiên, hãy tạo thư mục để lưu trữ các tệp Markdown:
bash
mkdir -p content/posts
Tiếp theo, thêm một bài viết Markdown mẫu vào thư mục này:
markdown
# content/posts/my-first-post.md
---
title: "Bài Viết Đầu Tiên"
date: "2023-10-01"
slug: "bai-viet-dau-tien"
highlight: true
---
Đây là một bài viết mẫu sử dụng **Markdown**.
Mỗi bài viết sẽ bao gồm siêu dữ liệu (title, date, slug, highlight) và nội dung Markdown thực tế.
Tạo Trang Chủ và Trang Chi Tiết Bài Viết
Trong App Router, chúng ta sẽ định nghĩa một component trang chủ trong src/app/page.tsx
. Mục tiêu là sử dụng hàm getPosts
để lấy và hiển thị tất cả các bài viết sử dụng Fusionable.
javascript
import Link from 'next/link';
import FusionCollection from 'fusionable/FusionCollection';
function getPosts() {
const collection = new FusionCollection()
.loadFromDir('content/posts')
.orderBy('date', 'desc');
return collection.getItemsArray();
}
export default function HomePage() {
const posts = getPosts(); // Static generation by default
return (
<main>
<h1>Danh Sách Các Bài Viết</h1>
<ul>
{posts.map((post) => (
<li key={post.fields.slug}>
<Link href={`/posts/${post.fields.slug}`}>{post.fields.title}</Link>
<p>{post.fields.date}</p>
</li>
))}
</ul>
</main>
);
}
Tạo Trang Bài Viết Động
Trong App Router, các đường dẫn động được định danh bằng cách sử dụng cấu trúc thư mục [slug]
. Tạo cấu trúc như sau:
src/
app/
posts/
[slug]/
page.tsx
Trong src/app/posts/[slug]/page.tsx
, chúng ta sẽ viết mã để tải và hiển thị một bài viết cụ thể:
javascript
import FusionCollection from "fusionable/FusionCollection";
import { FusionFieldsType, FusionItemType } from "fusionable/FusionItem";
import Showdown from 'showdown';
function getPostBySlug(slug: string): FusionItemType {
const collection = new FusionCollection().loadFromDir('content/posts');
const post = collection.getOneBySlug(slug);
if (!post) {
throw new Error('Không tìm thấy bài viết');
}
return post.getItem();
}
export default function PostPage({ params }: { params: { slug: string } }) {
const post = getPostBySlug(params.slug); // Static generation by default
const fields: FusionFieldsType = post.fields;
const converter = new Showdown.Converter();
const contentHTML = converter.makeHtml(post.content);
return (
<>
<h1>{fields.title}</h1>
<p>{fields.date}</p>
<div dangerouslySetInnerHTML={{ __html: contentHTML }} />
</>
);
}
Chuyển Đổi Markdown Sang HTML Với Showdown
Trong phần mã trên, Showdown được sử dụng để chuyển đổi nội dung Markdown sang HTML như sau:
javascript
import Showdown from 'showdown';
const converter = new Showdown.Converter();
const contentHTML = converter.makeHtml(post.content);
Nội dung HTML này sau đó được hiển thị bằng cách sử dụng dangerouslySetInnerHTML
để đảm bảo render đúng cách.
Cấu Trúc Thư Mục Sau Khi Hoàn Thành
Dưới đây là cấu trúc thư mục của dự án sau khi hoàn thành các bước:
my-nextjs-site/
├── src/app/
│ ├── page.tsx
│ ├── posts/
│ │ ├── [slug]/
│ │ │ ├── page.tsx
├── content/
│ ├── posts/
│ │ ├── my-first-post.md
Kết Luận
Sau khi thực hiện các bước trên, bạn đã xây dựng thành công một website tĩnh hoàn chỉnh với nội dung từ Markdown, khả năng lọc và sắp xếp nội dung nhờ Fusionable, cũng như chuyển đổi Markdown sang HTML bằng Showdown. Dự án này mở ra khả năng phát triển thêm với các đặc điểm như danh mục, thẻ hoặc bộ lọc để làm cho website trở nên phong phú và năng động hơn.
source: viblo