The two main reasons are that they often rely on
dangerouslySetInnerHTML
or have bugs with how they handle markdown.react-markdown
uses a syntax tree to build the virtual dom which allows for updating only the changing DOM instead of completely overwriting.react-markdown
is 100% CommonMark (optionally GFM) compliant and has extensions to support custom syntax.
本文将介绍react-markdown的配置,以及部分实用组件的配置和介绍,包括Katex、syntax-highlighter、Gfm以及TOC。react-markdown的源码和完全介绍请移步https://github.com/remarkjs/react-markdown
使用npm install react-markdown
引入react-markdown包,注意该组件的旧版本和新版本使用配置差别较大,我目前为v7.1.0
。
一个简单的hello world:
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'
ReactDom.render(<ReactMarkdown># Hello, *world*!</ReactMarkdown>, document.body)
如此以来,简单的react-markdown就配置好啦!但是这种配置支持的语法较少,如表格、代码高亮、数学公式等均不支持,且无法实现同步预览效果。
我使用的是redux来控制状态,当然也可以使用dva或者其他方式。
import React, { Component } from 'react'
import styles from './MdEditorComponent.module.css'
import { connect } from 'react-redux'
import { editMarkdown } from '../../../redux/sync-actions'
import ReactMarkdownComponent from '../article-component/ReactMarkdownComponent'
const DEFAULT_SIZE = 500 //默认代码块高度
class MdEditorComponent extends Component {
constructor (props) {
super(props)
this.state = {
textareaHeight: DEFAULT_SIZE
}
}
componentDidMount () {
window.addEventListener('resize', this.handleResize) //监听resize
}
handleResize = e => {
}
//修改内容状态
handleInputMarkdown = e => {
let previewHeight = this.preview.scrollHeight
let textareaHeight = e.target.scrollHeight
let height = Math.max(previewHeight, textareaHeight, DEFAULT_SIZE)
this.setState({ textareaHeight: height })
this.props.editMarkdown(e.target.value)
}
//当鼠标选中textarea时
handleKeyPressMarkdown = e => {
let previewHeight = this.preview.scrollHeight
let textareaHeight = e.target.scrollHeight
let height = Math.max(previewHeight, textareaHeight, DEFAULT_SIZE)
//下面是自定义的特殊按键触发的效果
if (e.keyCode === 9) {
e.preventDefault()
this.props.editMarkdown(this.props.markdown + '\t')
} else if (e.keyCode === 13)
return false
else if (e.keyCode === 17) { //left ctrl
this.setState({ textareaHeight: height })
} else if (e.keyCode === 8) { //backspace
this.setState({ textareaHeight: height })
} else if (e.keyCode === 192) { // ~
// e.preventDefault()
}
}
render () {
return (
<div className={styles.container} style={{
height: this.state.textareaHeight //设置默认高度
}}>
<div className={styles.input}>
<textarea style={{ border: '0px' }} spellCheck={false} value={this.props.markdown}
onKeyDown={this.handleKeyPressMarkdown}
onChange={this.handleInputMarkdown}/>
</div>
<div ref={div => this.preview = div} className={styles.md}>
<ReactMarkdownComponent content={this.props.markdown}/>
</div>
</div>
)
}
}
//使用redux来控制状态
MdEditorComponent = connect(
({ $BlogState: { mdContent } }) => ({ markdown: mdContent }),
{ editMarkdown }
)(MdEditorComponent)
export default MdEditorComponent
export function $BlogState (state = initBlogState, { data, type }) {
switch (type) {
case EDIT_MARKDOWN:
return { ...state, mdContent: data }
default:
return state
}
}
初始的显示内容
export const initBlogState = {
mdContent: `# Markdown Online Editor
## Subtitle
common content
### Little title
Code Block
### Code Syntax Highlight
~~~js
console.log('hello randyzhang')
~~~
`,
}
export const EDIT_MARKDOWN = 'edit_markdown'
export const editMarkdown = data => actionFactory(EDIT_MARKDOWN, data)
import { applyMiddleware, combineReducers, createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { $BlogState } from './reducers'
const store = createStore(
combineReducers({
$BlogState,
}),
composeWithDevTools(applyMiddleware(thunk))
)
export default store
配置好后即可使用表格、删除线、任务列表、引用等操作。需要使用npm install remark-Gfm
引入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
)
展示的效果为:
A paragraph with emphasis and strong importance.
A block quote with strikethrough and a URL: https://reactjs.org.
- Lists
- todo
- done
A table:
a b
使用该组件可以实现代码块的彩色,需要使用npm install react-syntax-hightlighter
--save来引入react-syntax-highlighter包。
这里components中间的部分主要是针对输入的代码块语言匹配相应的代码高亮,如果非匹配成功则直接使用code形式展示。
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
const markdown = `Here is some JavaScript code:
~~~js
console.log('It works!')
~~~
`
ReactDom.render(
<ReactMarkdown
children={markdown}
components={{
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>
)
}
}}
/>,
document.body
)
使用该组件可以轻松的翻译输入的数学公式,输入npm install remark-math rehype-katex
引入相应的包。
注意:需要使用
katex.css
来展示相应的效果,否则会出现BUG需要在index.html中添加上下列的话并下载包:
<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'
const markdown = ` L = \frac{1}{2} \rho v^2 S C_L`
ReactDom.render(
<ReactMarkdown
children={markdown}
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
/>,
document.body
)
展示的效果为:
L = 1 2 ρ v 2 S C L L = \frac{1}{2} \rho v^2 S C_L L=21ρv2SCL
该配置可以帮你自动生成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
)
如果你trusted environment (you trust the markdown)
,可以使用该配置使markdown支持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
)
有时候我们需要修改一些颜色、大小等,则可以按照一下规则进行修改:
<ReactMarkdown
components={{
// Map `h1` (`# heading`) to use `h2`s.
h1: 'h2',
// Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
em: ({node, ...props}) => <i style={{color: 'red'}} {...props} />
}}
/>
react-markdown
作为一款适用于react的markdown组件,使用非常方便,且丰富的remark和rehype可以扩展很多功能,自己也可以定义remark组件,推荐大家来使用。
如果有好的使用方法或者更适合的组件也可以告诉我,互相学习和进步~