從0到1搭建技術(shù)博客,Next.js Tailwind CSS MDX全流程
本文目錄導(dǎo)讀:
在當(dāng)今互聯(lián)網(wǎng)時(shí)代,擁有一個(gè)個(gè)人技術(shù)博客不僅能幫助開發(fā)者記錄學(xué)習(xí)歷程,還能提升個(gè)人品牌影響力,許多開發(fā)者可能會(huì)因?yàn)榧夹g(shù)選型或搭建過程的復(fù)雜性而望而卻步,本文將詳細(xì)介紹如何從零開始搭建一個(gè)現(xiàn)代化的技術(shù)博客,使用 Next.js 作為前端框架,Tailwind CSS 進(jìn)行樣式設(shè)計(jì),并支持 MDX 格式的博客內(nèi)容管理,我們將涵蓋從項(xiàng)目初始化到部署的全流程,幫助你快速構(gòu)建一個(gè)高性能、可擴(kuò)展的技術(shù)博客。

技術(shù)選型與項(xiàng)目初始化
1 為什么選擇 Next.js + Tailwind CSS + MDX?
- Next.js:基于 React 的框架,支持 SSR(服務(wù)端渲染)和 SSG(靜態(tài)生成),適合 SEO 優(yōu)化,并提供優(yōu)秀的開發(fā)體驗(yàn)。
- Tailwind CSS:一個(gè)實(shí)用優(yōu)先的 CSS 框架,可以快速構(gòu)建響應(yīng)式 UI,無需編寫大量自定義 CSS。
- MDX:Markdown 的擴(kuò)展,允許在 Markdown 中嵌入 React 組件,適合技術(shù)博客的內(nèi)容管理。
2 初始化項(xiàng)目
使用 create-next-app 快速搭建 Next.js 項(xiàng)目:
npx create-next-app@latest my-tech-blog --typescript cd my-tech-blog
安裝 Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p
配置 tailwind.config.js:
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
在 globals.css 中引入 Tailwind:
@tailwind base; @tailwind components; @tailwind utilities;
搭建博客核心功能
1 文章管理與 MDX 集成
安裝 @next/mdx 和 remark 相關(guān)插件:
npm install @next/mdx @mdx-js/loader @mdx-js/react remark-gfm rehype-highlight
在 next.config.js 中配置 MDX:
const withMDX = require("@next/mdx")({
extension: /\.mdx?$/,
options: {
remarkPlugins: [require("remark-gfm")],
rehypePlugins: [require("rehype-highlight")],
},
});
module.exports = withMDX({
pageExtensions: ["ts", "tsx", "md", "mdx"],
});
創(chuàng)建 posts 目錄存放 MDX 文件,示例 hello-world.mdx:
--- "Hello World"
date: "2023-10-01"
description: "我的第一篇博客文章"
---
# Hello World
這是我的第一篇博客文章,使用 **MDX** 編寫!
```js
console.log("Hello, MDX!");
### **2.2 實(shí)現(xiàn)文章列表與詳情頁**
#### **文章列表頁 (`pages/index.tsx`)**
```tsx
import { GetStaticProps } from "next";
import fs from "fs";
import path from "path";
import matter from "gray-matter";
interface Post {
slug: string;
frontmatter: { string;
date: string;
description: string;
};
}
export default function Home({ posts }: { posts: Post[] }) {
return (
<div className="max-w-3xl mx-auto p-4">
<h1 className="text-3xl font-bold mb-6">技術(shù)博客</h1>
<ul className="space-y-4">
{posts.map((post) => (
<li key={post.slug} className="border-b pb-4">
<a href={`/posts/${post.slug}`} className="block hover:text-blue-500">
<h2 className="text-xl font-semibold">{post.frontmatter.title}</h2>
<p className="text-gray-600">{post.frontmatter.date}</p>
<p className="text-gray-500">{post.frontmatter.description}</p>
</a>
</li>
))}
</ul>
</div>
);
}
export const getStaticProps: GetStaticProps = async () => {
const postsDir = path.join(process.cwd(), "posts");
const filenames = fs.readdirSync(postsDir);
const posts = filenames.map((filename) => {
const filePath = path.join(postsDir, filename);
const fileContent = fs.readFileSync(filePath, "utf-8");
const { data } = matter(fileContent);
return {
slug: filename.replace(/\.mdx$/, ""),
frontmatter: data,
};
});
return { props: { posts } };
};
文章詳情頁 (pages/posts/[slug].tsx)
import { GetStaticProps, GetStaticPaths } from "next";
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { MDXRemote } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
interface PostProps {
frontmatter: { string;
date: string;
description: string;
};
mdxSource: any;
}
export default function Post({ frontmatter, mdxSource }: PostProps) {
return (
<div className="max-w-3xl mx-auto p-4">
<h1 className="text-3xl font-bold mb-2">{frontmatter.title}</h1>
<p className="text-gray-600 mb-6">{frontmatter.date}</p>
<article className="prose prose-lg">
<MDXRemote {...mdxSource} />
</article>
</div>
);
}
export const getStaticPaths: GetStaticPaths = async () => {
const postsDir = path.join(process.cwd(), "posts");
const filenames = fs.readdirSync(postsDir);
const paths = filenames.map((filename) => ({
params: { slug: filename.replace(/\.mdx$/, "") },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
const filePath = path.join(process.cwd(), "posts", `${params?.slug}.mdx`);
const fileContent = fs.readFileSync(filePath, "utf-8");
const { data, content } = matter(fileContent);
const mdxSource = await serialize(content);
return { props: { frontmatter: data, mdxSource } };
};
優(yōu)化與部署
1 樣式優(yōu)化
- 使用
@tailwindcss/typography優(yōu)化文章排版:npm install @tailwindcss/typography
在
tailwind.config.js中添加:plugins: [require("@tailwindcss/typography")],
2 SEO 優(yōu)化
在 推薦使用 Vercel(Next.js 官方托管平臺(tái)): 本文詳細(xì)介紹了如何從零開始搭建一個(gè)基于 Next.js + Tailwind CSS + MDX 的技術(shù)博客,涵蓋: 通過這個(gè)流程,你可以快速搭建一個(gè)高性能、現(xiàn)代化的個(gè)人博客,并專注于內(nèi)容創(chuàng)作,希望本文對你有所幫助!??pages/_app.tsx 中添加 <Head>
import Head from "next/head";
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>我的技術(shù)博客</title>
<meta name="description" content="記錄技術(shù)學(xué)習(xí)與思考" />
</Head>
<Component {...pageProps} />
</>
);
}
3 部署