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

相关推荐
abc80021170342 小时前
前端Bug 修复手册
前端·bug
Best_Liu~2 小时前
el-table实现固定列,及解决固定列导致部分滚动条无法拖动的问题
前端·javascript·vue.js
_斯洛伐克3 小时前
下降npm版本
前端·vue.js
苏十八4 小时前
前端进阶:Vue.js
前端·javascript·vue.js·前端框架·npm·node.js·ecmascript
st紫月4 小时前
用MySQL+node+vue做一个学生信息管理系统(四):制作增加、删除、修改的组件和对应的路由
前端·vue.js·mysql
乐容5 小时前
vue3使用pinia中的actions,需要调用接口的话
前端·javascript·vue.js
似水明俊德5 小时前
ASP.NET Core Blazor 5:Blazor表单和数据
java·前端·javascript·html·asp.net
至天6 小时前
UniApp 中 Web/H5 正确使用反向代理解决跨域问题
前端·uni-app·vue3·vue2·vite·反向代理
与墨学长6 小时前
Rust破界:前端革新与Vite重构的深度透视(中)
开发语言·前端·rust·前端框架·wasm
H-J-L7 小时前
Web基础与HTTP协议
前端·http·php