我们先安装 npm install --save react-markdown
引入到项目中,npm官网有介绍用法。
react-markdown有很多属性可以自定义,完整代码如下:
import React from 'react';
import moment from "moment";
import ReactMarkdown from "react-markdown";
// 这里引入自定义组件
import {code, h1, h2, h3, h4, a, blockquote, li} from './Markdown'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
import Link from "next/link";
const PostDetail = ({post}) => {
return (
...
// tailwindcss
<div className="lg:p-4 p-2">
<ReactMarkdown
className=""
// 传入markdown文本内容
children={post.content}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
components={{
code: code,
h1: h1,
h2: h2,
h3: h3,
h4: h4,
a: a,
blockquote: blockquote,
li:li
}}
/>
</div>
);
};
export default PostDetail;
由上,我们的自定义组件样式都在Markdown.jsx中
,下面来看看:
import React from 'react'; import {Prism as SyntaxHighlighter} from "react-syntax-highlighter"; // 注意,这里有坑 import {oneDark as codeStyle} from "react-syntax-highlighter/dist/cjs/styles/prism"; // 定义一个代码块样式,官网给出的案例 export const code = ({node, inline, className, children, ...props}) => { const match = /language-(\w+)/.exec(className || '') // 判断是行内代码,还是独立代码块 return !inline && match ? ( <SyntaxHighlighter children={String(children).replace(/\n$/, '')} style={codeStyle} language={match[1]} PreTag="div" {...props} /> ) : ( <span className="text-sm mx-1" style={{color: '#c7254e', backgroundColor: '#f9f2f4', borderRadius: '2px'}}> {children} </span> //
) } // h1组件自定义 export const h1 = ({node, ...props}) => { return ( <div className="text-2xl mt-5 mb-3 font-bold" {...props} /> ); }; export const h2 = ({node, ...props}) => { return ( <div className="text-xl mt-3 mb-1 font-bold" {...props} /> ); }; export const h3 = ({node, ...props}) => { return ( <div className="text-lg mt-2 mb-1 font-bold" {...props} /> ); }; export const h4 = ({node, ...props}) => { return ( <div className="mt-2 mb-1 font-bold" {...props} /> ); }; // 链接组件自定义 export const a = ({node, ...props}) => { return ( <a href={node.properties.href} target="_blank" className="text-blue-600 hover:text-blue-500 mx-1 rounded-sm hover:shadow-md font-serif underline cursor-pointer" {...props} /> ); }; // 引用组件自定义 export const blockquote = ({node, ...props}) => { return ( <div className="my-2 bg-gray-300 shadow-md font-serif rounded-lg p-4" {...props}/> ); }; // li组件自定义 export const li = ({node, ...props}) => { // 注意,li的父组件可能为无序的// {children} //
和有序的
// 根据不同的父组件渲染不同组件 // props中ordered属性判断是有序还是无序 // 如果有序props中的index为序号索引,一般加一显示 if (props.ordered) { return ( <div className="flex shadow-md my-2 font-serif rounded-lg p-1"> {props.index + 1}. <div className="ml-2" {...props}/> </div> ) } // 无序渲染一个svg return ( <div className="flex items-center shadow-md my-2 font-serif rounded-lg p-1 "> <span> <svg t="1669231142038" onClick={() => { window.open("https://www.mcdonalds.com.cn/") }} className="icon h-5 w-5 mr-2 hover:cursor-pointer hover:scale-105 transition duration-75" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8368" width="200" height="200"> <path>...</path> </svg> </span> <div {...props}/> </div> ); };
注意,上面引入import {oneDark as codeStyle} from "react-syntax-highlighter/dist/cjs/styles/prism";
有个坑。官网让我们引入import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
,但是按照官网引入会报错,参见 StackOverflow : SyntaxError: Unexpected token ‘export’ in Next.js
另外,对于react-markdown
组件中katex
公式显示,存在版本bug,会导致公式显示乱码,且react报错如下:
Hydration failed because the initial UI does not match what was rendered on
经测试,是Next版本原因,我从 13.0.4 版本降到 13.0.0版本后,公式显示正常,react版本不变 : "react": "18.2.0","react-dom": "18.2.0"
。
这样,一个简单的markdown显示器完成: