• 基于react-markdown组件自定义一个Markdown显示器


    我们先安装 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    由上,我们的自定义组件样式都在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>
            // 
            //     {children}
            // 
        )
    }
    
    // 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的父组件可能为无序的
      和有序的
        // 根据不同的父组件渲染不同组件 // 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> ); };
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
        • 63
        • 64
        • 65
        • 66
        • 67
        • 68
        • 69
        • 70
        • 71
        • 72
        • 73
        • 74
        • 75
        • 76
        • 77
        • 78
        • 79
        • 80
        • 81
        • 82
        • 83
        • 84
        • 85
        • 86
        • 87
        • 88
        • 89
        • 90
        • 91
        • 92
        • 93
        • 94
        • 95
        • 96
        • 97
        • 98
        • 99
        • 100

        注意,上面引入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
        
        • 1

        经测试,是Next版本原因,我从 13.0.4 版本降到 13.0.0版本后,公式显示正常,react版本不变 : "react": "18.2.0","react-dom": "18.2.0"

        这样,一个简单的markdown显示器完成:

        在这里插入图片描述

        在这里插入图片描述

      1. 相关阅读:
        20220809
        计算机网络--应用层(http)
        三、内存管理 (一)内存管理基础
        C语言学习:11、数组
        001、DM8安装
        Django 用相对路径方式引用自定义模块 或 文件
        2.Spring的优缺点是什么?
        C++ 产生随机数函数
        机器学习笔记 十八:基于3种方法的随机森林模型分析房屋参数重要性
        从MediaRecord录像中读取H264参数
      2. 原文地址:https://blog.csdn.net/weixin_41866717/article/details/128016238