作为一名新时代农民工,几乎每个人都有自己的博客站点,而博客最重要的功能就是文档编辑,实现文档编辑无外乎Markdown富文本编辑器两种模式。其中富文本编辑器使用起来比较简单,不需要额外了解各种语法,对非技术人员来说容易上手,本文就着重介绍一下富文本编辑器框架————Slate,它不仅可以帮助我们自定义实现富文本效果,而且还能实现类似于Markdown一样的语法输入。

说起Slate,可能很多人不了解,但是说起前端富文本插件库wangEditor以及飞书文档,大家就不陌生了,这两款产品底层技术采用的就是Slate

安装

Slate 本身是支持各种框架的,但是官方提供了React版本的库实现,我们就以React版本示例:

 1npm install slate slate-react

初始化编辑器

初始化编辑节点,并赋予编辑器默认内容。

注意默认内容的数据结构需要符合Slate的格式,其基本元素类型类似于HTML,包括块节点(Blocks)、行内节点(inlines)。可以通过type字段区分不同的元素,后续我们的自定义节点就可以通过该字段区分。

 1// index.js 
 2import { useState } from "react";  
 3import { createEditor } from "slate"; 
 4import { Slate, Editable, withReact } from "slate-react"; 
 5import './index.css'  
 6const SlateBox = (  ) => {   
 7  const [editor] = useState(() => withReact(createEditor()))   
 8  const [editorText, setEditorText] = useState([   
 9    {      
10      type: 'paragraph',     
11      children: [{ 
12        text: '这是一个slate内容的结构示例,每一段都是通过文本节点或者自定义节点的数组组成,文本节点必须包含text属性。' }]   
13    }   
14  ])    
15  return (   
16    <Slate editor={editor} initialValue={editorText}>   
17    <Editable className="editor-container" />    
18      </Slate>  
19)
20} 
21export default SlateBox;

优化编辑器样式:

 1/* index.css */ 
 2*{   
 3  box-sizing: border-box; 
 4} 
 5.editor-container{ 
 6  width: 100vw;  
 7  height: 100vh;  
 8  padding: 8px;  
 9  background: #000; 
10  color: #fff; 
11}

目前的效果图:

添加工具栏

富文本编辑器就是通过各种快捷按钮实现文本的不同效果,因此我们在编辑器顶部添加一个工具栏,展示一个高亮块按钮。

 1js
 2
 3复制代码
 4
 5`// index.js import { useState } from "react";  import { createEditor } from "slate"; import { Slate, Editable, withReact } from "slate-react"; import { Button } from 'antd' import { BulbOutlined } from '@ant-design/icons' import './index.css'  const SlateBox = (  ) => {   const [editor] = useState(() => withReact(createEditor()))   const [editorText, setEditorText] = useState([     {       type: 'paragraph',       children: [{ text: '这是一个slate内容的结构示例,每一段都是通过文本节点或者自定义节点的数组组成,文本节点必须包含text属性。' }]     }   ])    const addHighBlock = (  ) => {     console.log('添加高亮块')   }    return (     <Slate editor={editor} initialValue={editorText}>       <div className="editor-toolbar">         <Button icon={<BulbOutlined />} className="menu-btn" type="text" onClick={addHighBlock}></Button>       </div>       <Editable className="editor-container" />     </Slate>   ) }  export default SlateBox;`

实现工具栏样式:

 1css
 2
 3复制代码
 4
 5`/* index.css */ *{   box-sizing: border-box; } .editor-toolbar{   background: #242426;   height: 40px;   display: flex;   align-items: center;   padding: 0 8px; } .menu-btn{   color: #fff !important; } .menu-btn:hover{   background: #000 !important; } .editor-container{   width: 100vw;   height: calc(100vh - 40px);   padding: 8px;   background: #000;   color: #fff;   border: 1px solid transparent; } .editor-container:focus-visible{   outline: none;   border-color: #fff; }`

如约而至的效果图:

重点来了,自定义我们的高亮节点

Slate提供了一个reanderElement方法,通过修改节点数据结构的type类型或者添加属性,就可以让我们轻松实现一个自定义节点。

 1js
 2
 3复制代码
 4
 5`// index.js import { useCallback, useState } from "react";  import { createEditor, Transforms } from "slate"; import { Slate, Editable, withReact, DefaultElement } from "slate-react"; import { Button } from 'antd' import { BulbOutlined } from '@ant-design/icons' import './index.css'  // 自定义高亮节点 const HighBlock = (props) => {   return (     // 添加slate相关属性     <div className="high-block" {...props.attributes}>       <BulbOutlined />       {/* 展示高亮节点下的内容 */}       {props.children}     </div>   ) }  const SlateBox = (  ) => {   const [editor] = useState(() => withReact(createEditor()))   const [editorText, setEditorText] = useState([     {       type: 'paragraph',       children: [{ text: '这是一个slate内容的结构示例,每一段都是通过文本节点或者自定义节点的数组组成,文本节点必须包含text属性。' }]     }   ])   const renderElement = useCallback(props => {     switch (props.element.type) {       case 'highBlock':         return <HighBlock {...props} />       default:         return <DefaultElement {...props} />     }   }, [])    const addHighBlock = (  ) => {     Transforms.insertNodes(editor, { type: 'highBlock', children: [{ text: '高亮节点默认值' }] })   }    return (     <Slate editor={editor} initialValue={editorText}>       <div className="editor-toolbar">         <Button icon={<BulbOutlined />} className="menu-btn" type="text" onClick={addHighBlock}></Button>       </div>       <Editable className="editor-container" renderElement={renderElement} />     </Slate>   ) }  export default SlateBox;`

添加我们自定义节点的样式:

 1css
 2
 3复制代码
 4
 5`/* index.css */ *{   box-sizing: border-box; } .editor-toolbar{   background: #242426;   height: 40px;   display: flex;   align-items: center;   padding: 0 8px; } .menu-btn{   color: #fff !important; } .menu-btn:hover{   background: #000 !important; } .editor-container{   width: 100vw;   height: calc(100vh - 40px);   padding: 8px;   background: #000;   color: #fff;   border: 1px solid transparent; } .editor-container:focus-visible{   outline: none;   border-color: #fff; } .high-block{   display: flex;   padding: 16px;   border-radius: 8px;   background: rgba(242, 150, 44, .17);   border: 1px solid #845117;   margin: 16px 0;   line-height: 24px; } .high-block .anticon-bulb{   color: #eb8f26;   line-height: 28px;   margin-right: 4px; }`

最后的实现效果图

以上只是一个Slate的简单入门示例,它还提供了许多诸如对选区、路径等操作方法,轻松实现滑动选择文字出现文字多音字提示等功能,大家可以去探索一下。

个人笔记记录 2021 ~ 2025