手把手教你造一個基於React的markdown編輯器

前言

筆者在18年年底的時候接到一個開發任務——搭建一個AI項目的開放平臺,其中的產品文檔爲轉化爲HTML格式的markdown文檔。考慮到文檔的即時更新,將文檔信息作成了Ajax接口的形式。所以管理後臺只需將textarea表單的內容經過markdown解析器進行HTML格式轉化,而後將markdown內容和經轉化的HTML文檔都保存到數據庫便可。css

基本需求完成後,爲了更好的用戶體驗,考慮將經常使用的編輯功能添加進來。改進版不只支持了經常使用的文本編輯功能,還實現的UI界面的配置化。本着造福伸手黨的目的,以及積累些開源經驗,筆者將該react 組件 react-markdown-editor-lite 進行了封裝改造,而且發佈到了開源社區。html

預覽

在線體驗 https://harrychen0506.github.io/react-markdown-editor-lite/node

image

特色

  • 輕量、基於React
  • UI可配置, 如只顯示編輯區或預覽區
  • 支持經常使用的markdown編輯功能,如加粗,斜體等等...
  • 支持編輯區和預覽區同步滾動

開發心得

  • 文本編輯

    大多數常見的編輯器,包括富文本編輯器,利用了某些元素如div的contenteditable屬性,配合selection、range、execCommand等API,實現了富文本編輯功能。這裏面的實現比較複雜,因此有了"爲何都說富文本編輯器是天坑?"這個說法。react

    而markdown編輯器,核心的處理內容爲簡單語法的純文本,複雜度相對來講比較低,而且input標籤自帶onSelect事件,能夠很方便的獲取選擇信息(選擇起始位置和選擇文本值),所以要想實現編輯功能,只需將要改動的內容進行文本轉換,而後進行從新拼接首尾,大功告成。git

  • markdown解析

    考察了幾個社區流行的markdown解析器,比較流行的有markdown, markdown-it, marked 等等。綜合考慮擴展性以及穩定性,筆者選擇了markdown-it做爲markdown的詞法解析器,結果也比較滿意。github

  • 同步滾動

    當選擇分欄編輯的時候,滾動左側的編輯區,右側的預覽區能自動滾動到對應的區域。方案參考了《手把手教你用 100行代碼實現基於 react的 markdown 輸入 + 即時預覽在線編輯器(一)》。只需先計算出輸入框容器元素與預覽框容器元素之間最大scroll範圍的比例值,而後根據主動滾動元素自身的scrollTop作相應的比例換算,便可知道對方區域的scrollTop值。數據庫

  • 關於UInpm

    • 項目的字體庫選擇了Font Awesome風格,而且只選取了項目所須要的一些圖標。
    • 編輯器的總體css都可經過全局覆蓋的形式進行自定義。目前暫時只支持灰色主題。
    • 編輯器的顯示區域包括菜單欄,編輯器,預覽區,工具欄,經過配置組件的config屬性,能夠選擇默認的展現區域。

Install

npm install react-markdown-editor-lite --save

Props

Property Description Type default Remarks
value markdown content String ''
style component container style Object {height: '100%'}
config component config Object {view: {...}, logger: {...}}
config.view component UI Object {menu: true, md: true, html: true}
config.imageUrl default image url String ''
config.linkUrl default link url String ''
config.logger logger in order to undo or redo Object {interval: 3000}
onChange emitting when editor has changed Function ({html, md}) => {}

Example

'use strict';
import React from 'react'
import ReactDOM from 'react-dom'
import MdEditor from 'react-markdown-editor-lite'

const mock_content = "Hello.\n\n * This is markdown.\n * It is fun\n * Love it or leave it."
export default class Demo extends React.Component {
  mdEditor = null
  handleEditorChange ({html, md}) {    
    console.log('handleEditorChange', html, md)
  }
  handleGetMdValue = () => {   
    this.mdEditor && alert(this.mdEditor.getMdValue())      
  }
  handleGetHtmlValue = () => {    
    this.mdEditor && alert(this.mdEditor.getHtmlValue())      
  }
  render() {
    return (      
      <div>
        <nav>
          <button onClick={this.handleGetMdValue} >getMdValue</button>  
          <button onClick={this.handleGetHtmlValue} >getHtmlValue</button>  
        </nav>
        <section style="height: 500px">
          <MdEditor 
            ref={node => this.mdEditor = node}
            value={mock_content}
            style={{height: '400px'}}
            config={{
              view: {
                menu: true,
                md: true,
                html: true
              },
              imageUrl: 'https://octodex.github.com/images/minion.png'
            }}
            onChange={this.handleEditorChange} 
          />
        </section>                        
      </div>      
    )
  }
}

最後

歡迎你們使用和反饋,項目地址 (https://github.com/HarryChen0..., 你的點贊將是我莫大的動力😊markdown

相關文章
相關標籤/搜索