在Next.js渲染Markdown竟然如此简单

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。

bash 复制代码
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 所需要的包:

bash 复制代码
pnpm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

在根目录创建一个 mdx-components.tsx 文件:

tsx 复制代码
import type { MDXComponents } from 'mdx/types'
 
export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
  }
}

这里解构的 components 可以是 HTML 标签,也可以是我们的 jsx/tsx 文件,比如我新建 components/button.tsx 文件:

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 文件:

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 文件:

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

markdown 复制代码
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

bash 复制代码
pnpm install next-mdx-remote

新建 components/mdx-components.tsx 文件:

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 文件:

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;
}
  1. 读取指定的MDX文件:通过网页的 URL 参数,使用 Node.js 的文件系统模块 fs 来读取 MDX 文件。
  2. 解析 MDX 文件:使用 serialize 函数将读取到的 MDX 文件内容进行解析和转化,可以将 MDX 格式的文件转化为可以在 React 中直接使用的 JSX 格式。
  3. 处理错误:如果在读取或解析文件的过程中出现错误(例如文件不存在),则会调用 notFound 函数进入 404 页面。
  4. 渲染 MDX 内容:使用自定义的 Mdx 组件将解析后的 MDX 内容进行渲染,展示在页面上。
  5. 提取MDX文件的元数据:generateMetadata 函数会提取 MDX 文件的 frontmatter 元数据,这些元数据通常用于存储一些关于文档的信息,如作者、创建日期等。
  6. generateMetadata函数导出,便可以在页面中渲染元数据。

新建 posts/mdx-remote.mdx 文件:

markdown 复制代码
---
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 渲染,有感兴趣的小伙伴可以关注一下。

相关推荐
Wiktok14 小时前
pureadmin的动态路由和静态路由
前端·vue3·pureadmin
devii6614 小时前
html.
前端
掘金安东尼14 小时前
为什么浏览器要限制 JavaScript 定时器?
前端·javascript·github
学前端搞口饭吃14 小时前
react context如何使用
前端·javascript·react.js
GDAL14 小时前
为什么Cesium不使用vue或者react,而是 保留 Knockout
前端·vue.js·react.js
IT_陈寒14 小时前
《Java 21新特性实战:5个必学的性能优化技巧让你的应用快30%》
前端·人工智能·后端
小谭鸡米花14 小时前
uni小程序中使用Echarts图表
前端·小程序·echarts
芜青14 小时前
【Vue2手录11】Vue脚手架(@vue_cli)详解(环境搭建+项目开发示例)
前端·javascript·vue.js
a别念m15 小时前
前端架构-CSR、SSR 和 SSG
前端·架构·前端框架
BillKu20 小时前
Vue3 + Element-Plus 抽屉关闭按钮居中
前端·javascript·vue.js