前端Markdown渲染(In React)
其實(shí)Typora是將markdown語(yǔ)法編譯成為了HTML,然后展現(xiàn)了出來(lái),比如單個(gè)#號(hào)編譯為了H1標(biāo)簽。
掘金和CSDN這類網(wǎng)頁(yè)也提供了markdown寫(xiě)作的功能。那么前端如何做一個(gè)markdown的富文本編輯器并且呈現(xiàn)實(shí)時(shí)預(yù)覽呢?
一套簡(jiǎn)單的技術(shù)棧是:marked.js實(shí)現(xiàn)markdown文本轉(zhuǎn)HTML,highlight.js對(duì)代碼進(jìn)行高亮。
但是如果你是一名React選手,那么你可以使用:
-
富文本編輯以及預(yù)覽:md-editor-rt -
markdown文本解析:react-markdown -
使markdown支持HTML語(yǔ)法:rehype-raw -
劃線、表、任務(wù)列表和直接url等的語(yǔ)法擴(kuò)展:remark-gfm -
代碼高亮:react-syntax-highlighter -
目錄提取與跳轉(zhuǎn):markdown-navbar
沒(méi)錯(cuò),我就是組件小子
下面來(lái)體驗(yàn)一下React的強(qiáng)大生態(tài)。
md-editor-rt富文本編輯器
下載
yarn add md-editor-rt
在React Hooks中,只需要非常簡(jiǎn)單的引入,使用useState配合Editor的兩個(gè)API,就可以實(shí)現(xiàn)輸入markdown并且實(shí)時(shí)渲染。
import React, { useState } from 'react';
// 導(dǎo)入組件
import Editor from 'md-editor-rt';
// 引入樣式
import 'md-editor-rt/lib/style.css';
export default function Md() {
const [text, setText] = useState('hello md-editor-rt!');
return <Editor modelValue={text} onChange={setText} />;
}
并且md-editor-rt自帶了Toolbar,也就是編輯框上方的輔助欄,可以幫助用戶更好的編輯。
React-markdown的渲染
上面說(shuō)的是markdown編輯器以及渲染,如果后端傳來(lái)了一個(gè)markdown文本,我們需要把它渲染到網(wǎng)頁(yè)上,就像我們點(diǎn)進(jìn)CSDN和掘金看文章,這時(shí)候需要對(duì)markdown做一個(gè)渲染,就可以用到react-markdown。下載
yarn add react-markdown
只需要把markdown的文本放到ReactMarkdown雙標(biāo)簽的組件就可以啦
import ReactMarkdown from "react-markdown";
<ReactMarkdown># Hello, *World*!</ReactMarkdown>
上方的Hello World!被解析為了一個(gè)h1標(biāo)簽。如果是單標(biāo)簽的話,我們可以把markdown的文本傳入children參數(shù)中。
import ReactMarkdown from "react-markdown";
<ReactMarkdown children={markdownText} />
引入插件支持
react-markdown并不支持所有的Markdown語(yǔ)法,但是我們可以使用插件來(lái)添加對(duì)這些語(yǔ)法的支持。
-
remake-plugin增加了對(duì)腳注、劃線、表、任務(wù)列表、自動(dòng)鏈接文字或直接url的支持 -
rehype-raw增加了對(duì)HTML原生語(yǔ)法的支持
import gfm from "remark-gfm";
import rehypeRaw from 'rehype-raw';
import gfm from 'remark-gfm';
<ReactMarkdown
children={text}
rehypePlugins={[rehypeRaw]}
remarkPlugins={[gfm]}
/>
react-syntax-highlighter
markdown中代碼會(huì)轉(zhuǎn)為code標(biāo)簽,但是HTML中對(duì)code其實(shí)沒(méi)有很好的樣式支持,所以我們需要額外的組件進(jìn)行代碼高亮。
我們可以定義一個(gè)組件(component),加入到react-markdown中。
import gfm from "remark-gfm";
import rehypeRaw from 'rehype-raw';
import gfm from 'remark-gfm';
<ReactMarkdown
children={text}
rehypePlugins={[rehypeRaw]}
remarkPlugins={[gfm]}
components={CodeBlock}
/>
其中CodeBlock是我們定義的插件。
首先,我們導(dǎo)入react-syntax-highlighter和一個(gè)代碼主題,這里是atomDark,你也可以選擇別的主題。
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
const CodeBlock = {
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter
children={String(children).replace(/\n$/, '')}
style={atomDark} // theme
language={match[1].toLowerCase()}
PreTag="section" // parent tag
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
};
下面的這一串code({ node, inline, className, children, ...props }) 可以看成是模板(因?yàn)槲乙膊惶芸吹枚呛竺娴拇a其實(shí)比較容易理解。
這里的match其實(shí)是抽取了代碼塊的代碼語(yǔ)言,match[1]的位置其實(shí)就是指定的語(yǔ)言,這里一個(gè)踩坑的點(diǎn):最好把字符串小寫(xiě)化。因?yàn)橛脩艨赡茌斎氲氖?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Java,但是SyntaxHighlighter其實(shí)只能識(shí)別java,所以會(huì)出現(xiàn)代碼沒(méi)有高亮的情況。
返回三元字符串的意思是如果語(yǔ)言支持,則使用SyntaxHighlighter高亮,如果不支持則直接返回code標(biāo)簽包裹。
markdown-navbar目錄提取
仍然是一個(gè)無(wú)腦的組件,你只需要將markdown文本傳入source參數(shù),就可以自動(dòng)提取標(biāo)題,并且點(diǎn)擊標(biāo)題可以實(shí)現(xiàn)跳轉(zhuǎn)和目錄高亮。
import MarkNav from 'markdown-navbar';
<MarkNav source={text} />
網(wǎng)頁(yè)案例
好了,你現(xiàn)在以及會(huì)markdown的渲染操作了,去寫(xiě)一個(gè)博客網(wǎng)頁(yè)的案例吧,只需要再加一點(diǎn)點(diǎn)細(xì)節(jié)。

歡迎關(guān)注這個(gè)摸魚(yú)更新的公眾號(hào)
