如何优雅的在 React 项目中使用 tui.editor

Yapi 使用 tui.editor 作为系统的文档编辑器,用于接口备注的编辑以及 wiki 模块,如果需要针对编辑器做一些调整,我们就会意识到之前的写法可维护性不太友好,加之 tui.editor@2.x 带来的优化,可以顺手做一次升级。

关于 tui.editor@2.x

关于 tui.editor@2.x,可以通过下面的链接获取更多的信息:

涉及开发的部分可以总结为下面三点:

  • npm 包更改到 @toast-ui 空间下
  • ext 更改为 plugin,并从核心库中拆分,作为单独的 npm 包
  • 2.x 提供了面向 React 和 Vue 的封装,封装逻辑比较简单,但对开发使用而言更简洁

需求

以上三点是我们在迁移 2.x 过程中需要注意的,开始动手前需要再梳理下我们的需求:

  • 编辑器需要支持语法高亮、颜色选择、表格、视频插件以及文件上传
  • 项目应该针对 EditorViewer 进行封装,对编辑器配置进行收敛,便于维护

关于视频插件可以查看之前的 tui.editor 视频嵌入插件

改造源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 封装 Editor
import React from 'react'
import { Editor as TuiEditor } from '@toast-ui/react-editor'
// 引入插件
import codeSyntaxHighlight from '@toast-ui/editor-plugin-code-syntax-highlight'
import hljs from 'highlight.js'
import tableMergedCell from '@toast-ui/editor-plugin-table-merged-cell'
import colorSyntax from '@toast-ui/editor-plugin-color-syntax'
import videoPlugin from '@leeonfield/editor-plugin-video'
// 引入语言包
import '@toast-ui/editor/dist/i18n/zh-cn'
// 引入样式
import 'codemirror/lib/codemirror.css'
import '@toast-ui/editor/dist/toastui-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'
import 'highlight.js/styles/github.css'
// 可选:从 highlight.js 中挑选一些常见语法进行支持
import javascript from 'highlight.js/lib/languages/javascript'
import bash from 'highlight.js/lib/languages/bash'
import c from 'highlight.js/lib/languages/c'
import cmake from 'highlight.js/lib/languages/cmake'
import java from 'highlight.js/lib/languages/java'
import json from 'highlight.js/lib/languages/json'
import less from 'highlight.js/lib/languages/less'
import css from 'highlight.js/lib/languages/css'
import php from 'highlight.js/lib/languages/php'
import go from 'highlight.js/lib/languages/go'
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('java', java)
hljs.registerLanguage('bash', bash)
hljs.registerLanguage('c', c)
hljs.registerLanguage('cmake', cmake)
hljs.registerLanguage('json', json)
hljs.registerLanguage('css', css)
hljs.registerLanguage('less', less)
hljs.registerLanguage('php', php)
hljs.registerLanguage('go', go)
// 可选:自定义图片上传方法
// const { uploadBlob } = require('common/utils.js')
const plugins = [
[codeSyntaxHighlight, { hljs }],
tableMergedCell,
[
colorSyntax,
{
preset: [
'#1abc9c',
'#2ecc71',
'#3498db',
'#9b59b6',
'#34495e',
'#f1c40f',
'#e67e22',
'#e74c3c',
'#ecf0f1',
'#95a5a6',
],
},
],
videoPlugin,
]

export default React.forwardRef((props, ref) => (
<TuiEditor
height="500px"
previewStyle="vertical"
initialEditType="markdown"
language="zh-CN"
usageStatistics={false}
placeholder="输入文档内容"
useCommandShortcut={false}
// hooks={{
// addImageBlobHook: function(blob, callback) {
// uploadBlob(blob, function(imgUrl) {
// callback(imgUrl, blob.name)
// })
// return false
// },
// }}
plugins={plugins}
{...props}
ref={ref}
/>
))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 封装 Viewer 
import React from 'react'
import { Viewer as TuiViewer } from '@toast-ui/react-editor'
// 引入插件
import codeSyntaxHighlight from '@toast-ui/editor-plugin-code-syntax-highlight'
import tableMergedCell from '@toast-ui/editor-plugin-table-merged-cell'
import hljs from 'highlight.js'
import videoPlugin from './videoPlugin'
// 引入样式
import 'codemirror/lib/codemirror.css'
import '@toast-ui/editor/dist/toastui-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'
import 'highlight.js/styles/github.css'
// 可选:从 highlight.js 中挑选一些常见语法进行支持
import javascript from 'highlight.js/lib/languages/javascript'
import bash from 'highlight.js/lib/languages/bash'
import c from 'highlight.js/lib/languages/c'
import cmake from 'highlight.js/lib/languages/cmake'
import java from 'highlight.js/lib/languages/java'
import json from 'highlight.js/lib/languages/json'
import less from 'highlight.js/lib/languages/less'
import css from 'highlight.js/lib/languages/css'
import php from 'highlight.js/lib/languages/php'
import go from 'highlight.js/lib/languages/go'
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('java', java)
hljs.registerLanguage('bash', bash)
hljs.registerLanguage('c', c)
hljs.registerLanguage('cmake', cmake)
hljs.registerLanguage('json', json)
hljs.registerLanguage('css', css)
hljs.registerLanguage('less', less)
hljs.registerLanguage('php', php)
hljs.registerLanguage('go', go)

const plugins = [
videoPlugin,
[codeSyntaxHighlight, { hljs }],
tableMergedCell,
]

export default React.forwardRef((props, ref) => (
<TuiViewer plugins={plugins} {...props} ref={ref} />
))

使用插件时,引入封装的组件,通过 ref 获取 editor 实例即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Editor 插件使用
import React, { Component } from 'react'
import Editor from './components/Editor'

export default class Untitled-2 extends Component {
constructor(props) {
super(props)
this.state = {
markdown: ''
}
this.editorRef = React.createRef()
}

componentDidMount() {
this.editor = this.editorRef.current.getInstance()
// 获取到实例后,可以调用官方提供的 api,如下
// 获取 HTML
const html = this.editor.getHtml()
// 获取 markdown
const markdown = this.editor.getMarkdown()
}

render() {
return (
<div>
<Editor
initialValue={this.state.markdown}
ref={this.editorRef}
/>
</div>
)
}
}