乐趣区

关于markdown:一款开源的Markdown转富文本编辑器的实现原理剖析

笔者平时写文章应用的都是 Markdown,然而公布的时候就会遇到一些平台不反对 Markdown 的状况,重排是不可能重排的,所以都会应用一些 Markdown 转富文本的工具,比方 markdown-nice,用的多了就会好奇是怎么实现的,于是就有了本篇文章。
markdown-nice 是一个基于 React 构建的我的项目,先来看一下它的整体页面:

一个顶部工具栏,两头三个并列的区域,别离是编辑区域、预览区域、自定义主题区域,自定义主题区域默认是暗藏的。
大体上就是一个 Markdown 编辑器,减少了一些对各个平台的适配而已。
编辑器
编辑器应用的是 CodeMirror,具体来说是一个二次封装的组件 React-CodeMirror:
import CodeMirror from “@uiw/react-codemirror”;

class App extends Component {

render() {
    return (
        <CodeMirror
              value={this.props.content.content}
              options={{
                theme: "md-mirror",// 主题
                keyMap: "sublime",// 快捷键
                mode: "markdown",// 模式,也就是语言类型
                lineWrapping: true,// 开启超长换行
                lineNumbers: false,// 不显示行号
                extraKeys: {// 配置快捷键
                  ...bindHotkeys(this.props.content, this.props.dialog),
                  Tab: betterTab,
                  RightClick: rightClick,
                },
              }}
              onChange={this.handleThrottleChange}
              onScroll={this.handleScroll}
              onFocus={this.handleFocus}
              onBlur={this.handleBlur}
              onDrop={this.handleDrop}
              onPaste={this.handlePaste}
              ref={this.getInstance}
            />
    )
}

}
复制代码
快捷键、命令
markdown-nice 通过 extraKeys 选项设置一些快捷键,此外还在工具栏中减少了一些快捷按钮:

这些快捷键或者命令按钮操作文本内容的逻辑根本是统一的,先获取以后选区的内容:
const selected = editor.getSelection()
复制代码
而后进行加工批改:
**${selected}**
复制代码
最初替换选区的内容:
editor.replaceSelection(**${selected}**)
复制代码
此外也能够批改光标的地位来晋升体验,比方加粗操作后光标地位会在文字前面,而不是 * 前面就是因为 markdown-nice 在替换完选区内容后还批改了光标的地位:

export const bold = (editor, selection) => {
editor.replaceSelection(**${selection}**);
const cursor = editor.getCursor();
cursor.ch -= 2;// 光标地位向前两个字符
editor.setCursor(cursor);
};
复制代码
表格
Markdown 的表格语法手写起来是比拟麻烦的,markdown-nice 对于表格只提供了帮你插入表格语法符号的性能,你能够输出要插入的表格行列数:

确认当前会主动插入符号:

实现其实就是一个字符串的拼接逻辑:
const text = this.buildFormFormat(this.state.rowNum, this.state.columnNum);

buildFormFormat = (rowNum, columnNum) => {

let formFormat = "";
// 起码会创立三行
for (let i = 0; i < 3; i++) {formFormat += this.buildRow(i, columnNum);
}
// 超过三行
for (let i = 3; i <= rowNum; i++) {formFormat += this.buildRow(i, columnNum);
}
return formFormat;

};

buildRow = (rowNum, columnNum) => {

let appendText = "|";
// 第一行为表头和内容的分隔
if (rowNum === 1) {
    appendText += "--- |";
    for (let i = 0; i < columnNum - 1; i++) {appendText += "--- |";}
} else {
    appendText += "|";
    for (let i = 0; i < columnNum - 1; i++) {appendText += "|";}
}
return appendText + (/windows|win32/i.test(navigator.userAgent) ? "\r\n" : "\n");

};
复制代码
表格字符生成当前替换以后选区内容即可:
handleOk = () => {

const {markdownEditor} = this.props.content;
const cursor = markdownEditor.getCursor();

const text = this.buildFormFormat(this.state.rowNum, this.state.columnNum);
markdownEditor.replaceSelection(text);

cursor.ch += 2;
markdownEditor.setCursor(cursor);
markdownEditor.focus();

};
复制代码
同样批改了光标地位并且让编辑器从新聚焦。

退出移动版