Next.js 作为一款开箱即用的 React 框架,因其优秀的服务器渲染能力和灵活的配置方式,已经吸引了大量的开发者。同时,Markdown 作为一种轻量级的标记语言,以其简洁的语法和强大的功能,已经成为了写作的首选工具。那么,如何在 Next.js 中渲染 Markdown 呢?这可能会让一些开发者感到困惑,但其实这个过程比你想象的要简单得多。在这篇文章中,我将会详细地解释如何在 Next.js 中渲染 Markdown,希望能为那些正在寻找解决方案的开发者提供一些参考和帮助。
什么是 Markdown
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换为有效的 HTML 文档。它可以用来制作说明文档、网页内容、电子书等。它的设计目标是易读性和易写性,以及尽可能的兼容性。Markdown 的语法由一些简单的符号组成,如井号(#)、星号(*)和破折号(-)。
MDX 则是一种扩展 Markdown 的语言,它允许你在 Markdown 文档中直接编写 JSX,并通过组件来导入和使用。这意味着你可以在 Markdown 中使用 React 组件,或者在 JSX 中使用 Markdown。MDX 非常适合用于创建复杂的交互式文档、幻灯片、博客等。
开始
项目基于 create-next-app 搭建,安装了 TailwindCSS。
PS E:\> pnpm create create-next-app@latest
√ What is your project named? ... next-mdx
√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... Yes
√ Would you like to use `src/` directory? ... No
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@/*)? ... No
安装渲染 MDX 所需要的包:
pnpm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
在根目录创建一个 mdx-components.tsx
文件:
import type { MDXComponents } from 'mdx/types'export function useMDXComponents(components: MDXComponents): MDXComponents {return {...components,}
}
这里解构的 components
可以是 HTML 标签,也可以是我们的 jsx/tsx 文件,比如我新建 components/button.tsx
文件:
const Button = ({ children }: { children: React.ReactNode }) => {return (<button className="test-xs px-4 py-2 bg-zinc-950 text-white rounded-md">{children}</button>);
};export default Button;
修改 mdx-components.tsx
文件:
import type { MDXComponents } from "mdx/types";
import Button from "@/components/button";export function useMDXComponents(components: MDXComponents): MDXComponents {return {...components,h1: (props) => <h1 className="text-3xl font-bold">{props.children}</h1>,Button,};
}
上面代码给 h1
标签添加了两个 class:text-3xl font-bold
设置一级标题字体大小与加粗,并且导入了 Button 组件。
修改根目录中的 next.config.js
文件:
const withMDX = require("@next/mdx")();/** @type {import('next').NextConfig} */
const nextConfig = {pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
};module.exports = withMDX(nextConfig);
然后就可以在项目中愉快的渲染 .mdx
文件了。
新建 src/mdx/page.mdx
:
export const metadata = {title: 'mdx'
}# mdx-remote- 1
- 2
- 3<Button>button</Button>
由于我们前面在 mdx-components.tsx
中添加了 Button 组件,于是可以直接在 mdx 中使用。
export const metadata
用于设置页面元数据,这些元数据对于SEO(搜索引擎优化)非常重要。
访问 /mdx
页面,你将会看到以下页面,并且网页的标题也被修改为了 mdx
:
Remote MDX
如果 Markdown 或 MDX 文件或内容位于其他位置,比如本地文件夹、远程 URL、数据库等,则需要在服务器上动态获取它。
有两个常用的库,一个是 next-mdx-remote
一个是 contentlayer
,接下来我将演示如何在 Next.js 中使用 next-mdx-remote
来渲染 mdx
文件。
安装 next-mdx-remote
:
pnpm install next-mdx-remote
新建 components/mdx-components.tsx
文件:
"use client";import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";
import Button from "./button";export default function Mdx(props: MDXRemoteProps) {return (<MDXRemote{...props}components={{h1: (props) => {return <h1 className="text-3xl font-bold">{props.children}</h1>;},Button,}}/>);
}
创建文件 app/[slug]/page.tsx
文件:
import { MDXRemote } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import fs from "fs";
import Mdx from "@/components/mdx-components";
import { notFound } from "next/navigation";interface PostProps {params: {slug: string;};
}const PostsPage = async ({ params: { slug } }: PostProps) => {try {const postFile = fs.readFileSync(`posts/${slug}.mdx`);const source = await serialize(postFile, { parseFrontmatter: true });return (<div><Mdx {...source} /></div>);} catch {notFound();}
};export default PostsPage;async function getPostBySlug(slug: string) {const postFile = fs.readFileSync(`posts/${slug}.mdx`);const source = await serialize(postFile, { parseFrontmatter: true });return source;
}export async function generateMetadata({ params }: PostProps) {const { frontmatter } = await getPostBySlug(params.slug);return frontmatter;
}
- 读取指定的MDX文件:通过网页的 URL 参数,使用 Node.js 的文件系统模块 fs 来读取 MDX 文件。
- 解析 MDX 文件:使用
serialize
函数将读取到的 MDX 文件内容进行解析和转化,可以将 MDX 格式的文件转化为可以在 React 中直接使用的 JSX 格式。 - 处理错误:如果在读取或解析文件的过程中出现错误(例如文件不存在),则会调用
notFound
函数进入 404 页面。 - 渲染 MDX 内容:使用自定义的
Mdx
组件将解析后的 MDX 内容进行渲染,展示在页面上。 - 提取MDX文件的元数据:
generateMetadata
函数会提取 MDX 文件的 frontmatter 元数据,这些元数据通常用于存储一些关于文档的信息,如作者、创建日期等。 - 将
generateMetadata
函数导出,便可以在页面中渲染元数据。
新建 posts/mdx-remote.mdx
文件:
---
title: mdx-remote
description: description
data: 2023-12-19
authro: 远小帅
---# mdx-remote- 1
- 2
- 3<Button>button</Button>
在文件开头我使用了三个破折号包括了一段数据,这被称为 Frontmatter,采用 YAML
格式,用来描述文档的元数据。
此时访问 /mdx-remote
页面,便可以看到和刚才图片相同的页面。
总结
在这篇文章中,我详细介绍了如何在 Next.js 中渲染 Markdown 和 MDX 文件。希望这篇文章能够帮助你更好地理解和使用 Markdown 和 MDX 在 Next.js 中的应用,为你的开发工作带来便利。
下一篇文章我将开发一个具有 SEO 优化的博客实战项目,使用 MDX 渲染,有感兴趣的小伙伴可以关注一下。