You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从MongoDB获取博客JSX内容并在NextJS中正常渲染

问题根源

你存入MongoDB的JSX内容本质是普通字符串,React只会在编译阶段把JSX语法转换成React.createElement调用,运行时从数据库拿到的字符串不会被自动解析为可渲染的React元素,所以会直接以原始文本形式展示。同时你现有代码还存在两个明显bug:

  • getStaticProps没有根据当前slug过滤文章,全量查询后固定取数组第一项,所有路径都会渲染第一篇内容
  • 重复使用JSON.parse(JSON.stringify())做序列化,没有正确处理MongoDB的ObjectId类型,还会造成不必要的性能损耗

可行解决方案

优先选择第一种MDX方案,安全性和性能表现更好;如果需要兼容存量已存储的JSX字符串,可以选择第二种运行时解析方案。

方案1:使用MDX存储博客内容(推荐)

MDX是支持嵌入JSX的Markdown格式,Next.js官方有成熟的解析工具链,支持自定义组件映射,你写在className里的Tailwind样式不需要额外配置就能正常生效。

实现步骤

  1. 安装依赖
npm install next-mdx-remote
  1. 调整数据库存储格式:将原有content字段的JSX内容改为MDX格式存储即可,原有JSX标签、className属性都可以保留,注意修正Next.js Image组件的错误属性(Image组件需要src而非href,且必须传alt、width、height)。
  2. 修改[postslug].js页面代码,修复原有逻辑并加入MDX解析能力:
import Head from 'next/head'
import Link from 'next/link'
import Image from 'next/image'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'
import { connectToDatabase } from "../../util/mongodb";

// 注册MDX中用到的自定义组件
const components = {
  Link,
  Image
}

export async function getStaticPaths() {
  const { db } = await connectToDatabase();
  // 只查询需要的slug字段,减少冗余数据传输
  const blogs = await db
    .collection("blogs")
    .find({}, { projection: { slug: 1, _id: 0 } })
    .toArray();

  const paths = blogs.map((p) => ({
    params: { postslug: p.slug }
  }));

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps(context) {
  const reqslug = context.params.postslug;
  const { db } = await connectToDatabase();
  // 按slug查询单篇文章,不要全量查询
  const post = await db
    .collection("blogs")
    .findOne({ slug: reqslug });

  // 序列化MDX内容供前端渲染
  const mdxSource = await serialize(post.content)

  return {
    props: {
      post: {
        ...post,
        _id: post._id.toString(), // 处理MongoDB ObjectId序列化问题
        content: mdxSource
      },
    },
  };   
}

const Post = ({post}) => {
  return (
    <>
      <Head>
        <title>{post.title}</title>
      </Head>
      <article className="max-w-3xl mx-auto py-10 px-4">
        <Image 
          src={post.featuredImage}
          alt={post.alt}
          width={Number(post.width)}
          height={Number(post.height)}
          className="w-full rounded-lg mb-8"
        />
        {/* 渲染MDX内容,传入自定义组件 */}
        <MDXRemote {...post.content} components={components} />
      </article>
    </>
  )
}

export default Post;

方案2:运行时解析已存储的JSX字符串(兼容存量数据)

如果暂时不想修改存量的JSX字符串数据,可以用react-jsx-parser在运行时解析字符串,注意:该方案仅可解析你自己后台录入的可信内容,绝对不要解析用户提交的内容,否则会存在XSS安全风险。

实现步骤

  1. 安装依赖
npm install react-jsx-parser
  1. 修改[postslug].js页面代码:
import Head from 'next/head'
import Link from 'next/link'
import Image from 'next/image'
import JsxParser from 'react-jsx-parser'
import { connectToDatabase } from "../../util/mongodb";

export async function getStaticPaths() {
  const { db } = await connectToDatabase();
  const blogs = await db
    .collection("blogs")
    .find({}, { projection: { slug: 1, _id: 0 } })
    .toArray();

  const paths = blogs.map((p) => ({
    params: { postslug: p.slug }
  }));

  return { paths, fallback: false };
}

export async function getStaticProps(context) {
  const reqslug = context.params.postslug;
  const { db } = await connectToDatabase();
  const post = await db.collection("blogs").findOne({ slug: reqslug });
  return {
    props: {
      post: {
        ...post,
        _id: post._id.toString()
      }
    },
  };   
}

const Post = ({post}) => {
  return (
    <>
      <Head>
        <title>{post.title}</title>
      </Head>
      <article className="max-w-3xl mx-auto py-10 px-4">
        <JsxParser
          // 注入用到的自定义组件
          components={{ Link, Image }}
          // 传入待解析的JSX字符串
          jsx={post.content}
          allowUnknownElements={true}
        />
      </article>
    </>
  )
}

export default Post;

内容的提问来源于stack exchange,提问作者Big Loot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:51:45