react-markdown的使用

安装

github-remarkjs-markdown

npm i react-markdown

基本使用

  • 表格、代码高亮、数学公式等均不支持
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'

const markdownData = `
    ### test header
`

<RactMarkdown>
    {markdownData}
</ReactMarkdown>


ReactDom.render(
    <ReactMarkdown># Hello, *world*!</ReactMarkdown>, 
    document.body
)

remark-gfm多功能

  • npm install remark-Gfm
  • 配置后,可使用表格、删除线、任务列表、引用等操作。
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'
import remarkGfm from 'remark-gfm'

const markdown = `A paragraph with *emphasis* and **strong importance**.

> A block quote with ~strikethrough~ and a URL: https://reactjs.org.

* Lists
* [ ] todo
* [x] done

A table:

| a | b |
| - | - |
`

ReactDom.render(
  <ReactMarkdown children={markdown} remarkPlugins={[remarkGfm]} />,
  document.body
)

highlighter语法高亮

  • 结合react-syntax-highlighter使得代码拥有语法高亮
npm i react-syntax-highlighter

使用

import React from "react";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { coldarkCold } from "react-syntax-highlighter/dist/esm/styles/prism";

const markdownData = `
~~~js
console.log('markdownData!')
~~~
`;
const code = ({ node, inline, className, children=[], ...props }) => {
  const match = /language-(\w+)/.exec(className || "");
  return !inline && match ? (
      <SyntaxHighlighter
        children={String(children).replace(/\n$/, '')}
        style={dark}
        language={match[1]}
        PreTag="div"
        {...props}
      />
    ) : (
      <code className={className} {...props}>
        {children}
      </code>
    )
};
    
const Index = () => {
  return (
    <ReactMarkdowncomponents={{code: code}}>
      {markdownData}
    </ReactMarkdown>
  );
};
export default Index

katex数学公式

  • npm install remark-math rehype-katex

// <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css" integrity="sha384-RZU/ijkSsFbcmivfdRBQDtwuwVqK7GMOw6IMvKyeWL2K5UAlyp6WonmB8m7Jd0Hn" crossorigin="anonymous">

import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import "katex/dist/katex.css";

const markdown = ` L = \frac{1}{2} \rho v^2 S C_L`

ReactDom.render(
  <ReactMarkdown
    children={markdown}
    remarkPlugins={[remarkMath]}
    rehypePlugins={[rehypeKatex]}
  />,
  document.body
)

remark-toc目录生成

  • 用于自动生成markdown的目录,仅需要在开头使用## Table of Content,即可以自动生成。
  • npm install remark-toc
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import remarkToc from 'remark-toc'

ReactDom.render(
  <ReactMarkdown
    children={markdown}
    remarkPlugins={[remarkToc]}
  />,
  document.body
)

rehype-raw HTML支持

  • npm install rehype-raw
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'

const input = `<div class="note">

Some *emphasis* and <strong>strong</strong>!

</div>`

ReactDom.render(
  <ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,
  document.body
)

自定义映射样式

  • html 元素映射fn返回组件或元素
<ReactMarkdown
  components={{
    h1: 'h2',
    em: ({node, ...props}) => <i style={{color: 'red'}} {...props} />
    p: () => <p style={{color: 'red'}} {...props} />
  }}
/>

流程图\数学公式

npm i mermaid
npm i katex
  • 最终使用
import ReactMarkdown from 'react-markdown'

const markdownData = `
    ### test header
`

<RactMarkdown
    components={{
        code: SpecialCode
    }}
>
    {markdownData}
</ReactMarkdown>

思路就是通过classname不同,判断code的类型,markdown就将 ```xxx 解析到classname中,并给一个language-xxx的classname,再通过classname使用不同的渲染器即可实现,下面将code函数的代码单独抽离出来

import React, { useRef, useEffect, useMemo, useCallback, useState } from "react";
import { CodeProps } from "react-markdown/lib/ast-to-react";
import mermaid from "mermaid";
import katex from "katex";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { coldarkCold } from "react-syntax-highlighter/dist/esm/styles/prism";
import "katex/dist/katex.css";

// <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css" integrity="sha384-RZU/ijkSsFbcmivfdRBQDtwuwVqK7GMOw6IMvKyeWL2K5UAlyp6WonmB8m7Jd0Hn" crossorigin="anonymous">


function randomId() {
  return parseInt(String(Math.random() * 1e15), 10).toString(36);
}

function getCode(arr: React.ReactNode[] = []): string {
  return arr
    .map(_dt => {
      const dt = _dt as any;

      if (typeof dt === "string") {
        return dt;
      }
      if (dt.props && dt.props.children) {
        return getCode(dt.props.children);
      }
      return false;
    })
    .filter(Boolean)
    .join("");
}

function RenderInline({ children = [] }: Omit<CodeProps, "inline">) {
  const txt = children[0] || "";
  if (typeof txt === "string" && /^\$\$(.*)\$\$/.test(txt)) {
    const html = katex.renderToString(txt.replace(/^\$\$(.*)\$\$/, "$1"), { throwOnError: false });
    return (
      <code
        style={{ backgroundColor: "rgb(227, 234, 242)", padding: "0.2rem", borderRadius: "0.2rem" }}
        dangerouslySetInnerHTML={{ __html: html }}
      />
    );
  }
  return <code>{txt}</code>;
}

function RenderBlock({ children = [], className, ...props }: Omit<CodeProps, "inline">) {
  const markId = useRef(`mark${randomId()}`);
  const code = getCode(children);

  const [transformCode, setTransformCode] = useState("");

  const codeType = useMemo(() => {
    if (typeof code !== "string" || typeof className !== "string") return;

    if (/^language-mermaid/.test(className.toLocaleLowerCase())) {
      return "mermaid";
    } else if (/^language-katex/.test(className.toLocaleLowerCase())) {
      return "katex";
    }
  }, [code, className]);

  const renderMermaid = useCallback(async (code: string) => {
    const { svg } = await mermaid.render(markId.current, code);

    setTransformCode(svg);
  }, []);

  const renderKatex = useCallback(async (code: string) => {
    const strCode = katex.renderToString(code, { throwOnError: false });

    setTransformCode(strCode);
  }, []);

  useEffect(() => {
    if (codeType === "mermaid") {
      renderMermaid(code);
    } else if (codeType === "katex") {
      renderKatex(code);
    }
  }, [code, codeType]);

  if (codeType === "mermaid") {
    return (
      <div className='code-block' style={{ textAlign: "center" }}>
        <code dangerouslySetInnerHTML={{ __html: transformCode }} />
      </div>
    );
  } else if (codeType === "katex") {
    return <code className='code-block' dangerouslySetInnerHTML={{ __html: transformCode }} />;
  }

  const match = /language-(\w+)/.exec(className || "");
  return (
    <SyntaxHighlighter
      language={match?.[1]}
      showLineNumbers={true}
      style={coldarkCold as any}
      PreTag='div'
      className='syntax-hight-wrapper'
      {...props}>
      {children as string[]}
    </SyntaxHighlighter>
  );
}

const SpecialCode = ({ inline, ...extra }: CodeProps) => {
  if (inline) return <RenderInline {...extra} />;

  return <RenderBlock {...extra} />;
};

export default SpecialCode;

扩展:markdown 编辑器

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值