浅析富文本编辑器框架Slate.js
浅析富文本编辑器框架Slate.js
本文不是关于Slate.js使用入门的文章,如果还不了解该框架,建议先阅读下官方的文档:Slate官网文档
关于Slate的一些特性
- 不同于其他编辑器类的库,Slate并不提供譬如粗体、斜体、字体色等开箱即用的功能
- Slate只是提供了一套自己定义的核心数据模型,以此一些操作数据和选区相关的API
- 视图层的渲染和行为完全由开发者基于React定制
从顶层设计上看,Slate的架构是典型的MVC模型,由自身定义数据模型(Model),暴露操作数据的方法(Controller),然后交由用户使用该数据在React中做渲染(View)
虽然在实现简单的编辑器应用时这种方式显得有些繁冗,但在遇到需要对业务做较定制化的功能,如内嵌复杂表单、流程图等时,就能展现出极大的灵活性。而这类需求在使用其他编辑器的库时,经常是不可行的或者成本很高(往往要在源码层面进行改造)
Slate的数据模型
Slate.js数据模型的设计非常的“类DOM”,对于拥有Web基础的开发者降低了心智负担。下面从节点(Node)和选区(Selection)的设计上说明。
type Node = Editor | Element | Text
interface Editor {
children: Node[]
}
interface Element {
children: Node[]
[key: string]: unknown
}
interface Text {
text: string,
[key: string]: unknown
}
Node作为最抽象的节点类型,包括以下三种类型:
Editor编辑器的根节点类型Element具有children属性,可以作为其他Node的父节点;由传入的renderElement函数做自定义渲染Text包含文本信息;由renderLeaf函数做自定义渲染;在添加mark时,将文本打散成不同的Leaf(这个行为是由Slate执行的,下面的例子会讲)
除了接口中定义的属性,也可以在节点中添加任意业务相关的属性值(如下面的例子)。
一个基础的使用示例如下:
const RichText = (props: any) => {
// 创建Editor
const editor = createEditor()
// 初始值
const [value, setValue] = useState([{
type: 'paragraph',
children: [{ text: "" }]
}])
// 自定义Element渲染
const renderElement = (props) => {
const { attributes, children, element } = props
switch (element.type) { // 根据Element中的type属性判断节点类型
case "heading-one":
return <h1 {...attributes}>{children}</h1>;
case "heading-two":
return <h2 {...attributes}>{children}</h2>;
case "paragraph":
return <p {...attributes}>{children}</p>;
}
}
// 自定义Leaf渲染
const renderLeaf = (props) => {
const { attributes, children, leaf } = props
// 根据Text中的自定义属性判断样式类型
if (leaf['background-color']) {
children = <span style={{ backgroundColor: leaf['background-color']}}>
{children}
</span>
}
if (leaf['font-color']) {
children = <span style={{ color: leaf['font-color']}}>
{children}
</span>
}
return <span {...attributes}>{children}</span>;
}
return <Slate editor={editor} value={value} onChange={(value) => setValue(value)}>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}/>
</Slate>
}
假如我们在该编辑器中输入了两行文字,并选取一段文本添加颜色样式:

则上图的文本内容所对应的数据结构:
[
{
type: "paragraph",
children: [
{
text: "著名武术泰斗马保国。"
}
]
},
{
type: "paragraph",
children: [
{
text: "著名"
},
{
text: "篮球运动员",
'font-size': "rgba(139, 87, 42, 1)"
},
{
text: "蔡徐坤"
}
]
}
]
该数组中最顶层的对象映射了Element元素(由renderElement渲染的),在其中的type字段中设为paragraph标志为默认的块级元素(当然也可以设为其他任何值,Slate.js并不关心type字段的含义)。下一层的叶子节点对象,包含了text字段,表示文本内容;也可能含有自定义的一些marks,例如上面的'font-size',用来在renderLeaf中依据marks来实现自定义的样式渲染。
接下来再选取一段文字赋予一个背景色:

添加背景色的选区是在上面带有font-size的Text节点中的,因此会被打散成三个Text,变为如下形式:
[
{
type: "paragraph",
children: [
{
text: "著名武术泰斗马保国。"
}
]
},
{
type: "paragraph",
children: [
{
text: "著名"
},
{
text: "篮球",
'font-size': "rgba(139, 87, 42, 1)"
},
{
text: "运动",
'font-size': "rgba(139, 87, 42, 1)",
'background-color': "rgba(80, 227, 194, 1)"
},
{
text: "员",
'font-size': "rgba(139, 87, 42, 1)"
},
{
text: "蔡徐坤"
}
]
}
]
光标和选区
光标的定位由一个 Path 和 offset 确定,Path 代表节点在文档中的位置,offset 则代表在节点中的偏移量:
declare type Path = number[]
interface Point {
path: Path,
offset: number
}
Paht 是一个number类型数组,包含的数值代表着从文档数据模型的根部到光标所在Text节点的路径;offset 表示光标在Text节点上的偏移量。
如图中框中的节点所对应的Path就是[1, 0]
选取的接口定义 Range 与原生selection的属性非常相似:
interface Range {
anchor: Point,
focus: Point
}
锚点anchor代表选区的起始点,焦点focus代表选区的结束点;两者都为上述的Point类型。
插件机制
Slate提供了插件的机制允许我们覆盖编辑器原有的行为。除了直接使用slate-react和slate-history这些官方的插件,也可以自定义插件来对Slate编辑器进行拓展,而且实现方式非常简易:提供一个函数,该函数接收一个编辑器的实例editor对象,在其中重写实例对象上的方法,并返回editor实例。
下面是个例子,加入在实现业务时有这么一个场景,需要在文本域中插入一些自定义的控件如按钮、下拉框等,并且都不可被编辑;而默认情况下在文本域中所有的dom元素都是contenteditable=true的状态,是能够被光标聚焦和编辑的。为了改变这种行为,可以自行实现一个插件:
import { createEditor } from "slate"
import { withReact } from "slate-react"
import { withHistory } from "slate-history"
const myCustomeEditor = (editor) => {
const { isVoid } = editor // editor原有的isVoid方法, 用以判断节点是否可编辑
editor.isVoid = (element) => { // 根据自定义的type字段将元素置为 不可编辑的
return element.type === 'custome-element' ? true : isVoid(element)
}
return editor
}
// 创建了一个带有三个插件组合的Slate编辑器
const eidotr = myCustomeEditor(withHistory(withReact(createEditor())))
浅析富文本编辑器框架Slate.js的更多相关文章
- 放弃WebView,使用Crosswalk做富文本编辑器
版权声明: 欢迎转载,但请保留文章原始出处 作者:GavinCT 出处:http://www.cnblogs.com/ct2011/p/4100132.html 为什么放弃WebView Androi ...
- 【重点突破】—— React实现富文本编辑器
前言:富文本编辑器Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器. 一.安装插件 react-draft-wysiwyg: 文本编辑器插件 dra ...
- 富文本编辑器kindeditor的使用
第一步:导入前端js文件 <!-- 富文本编辑器 --> <link rel="stylesheet" href="../plugins/kindedi ...
- js ui框架 My97日期控件 富文本编辑器
My97日期控件 http://www.my97.net/dp/index.asp 富文本编辑器 http://www.kindsoft.net/demo.php 百度的magic也不错 http:/ ...
- 百度富文本编辑器ueditor在jsp中的使用(ssm框架中的应用)
折腾了一下午终于把百度富文本编辑器ueditor搞定了! 项目地址:https://github.com/724888/lightnote_new 首先我参考了一个ueditor的demo ...
- JS编写自己的富文本编辑器
富文本编辑器,网上有很多功能齐全种类丰富的如百度的Ueditor,简单适用型的如WangEditor等等.在经过一番挑选后,我发现都不适用现在的项目,然后决定自己造轮子玩玩.富文本编辑器中主要涉及到J ...
- 富文本编辑器上传图片需要配置js,后台代码
富文本编辑器上传图片需要配置js,后台代码
- 一款纯HTML+CSS+JS富文本编辑器-handyeditor
官网:http://he.catfish-cms.com/ 修改版本(修改一些BUG和图片上传服务器 点击下载: handyeditor富文本编辑器.zip): 图片上传接口上传类型: Content ...
- Vue.js中使用wangEditor富文本编辑器
1.前端代码 前端HTML <script src="https://cdn.bootcss.com/wangEditor/10.0.13/wangEditor.js"> ...
随机推荐
- Linux_配置主DNS服务(基础)
[RHEL8]-DNSserver:[Centos7.4]-DNSclient !!!测试环境我们首关闭防火墙和selinux(DNSserver和DNSclient都需要) [root@localh ...
- SpringCloud(八)Sleuth 分布式请求链路跟踪
SpringCloud Sleuth 分布式请求链路跟踪 概述 为什么会出现这个技术?需要解决哪些问题? 在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后 ...
- 微服务系列(二)GRPC的介绍与安装
微服务系列(二)GRPC的介绍与安装 1.GPRC简介 GRPC是Google公司基于Protobuf开发的跨语言的开源RPC框架.GRPC基于HTTP/2协议设计,可以基于一个HTTP/2链接提供多 ...
- Python发送SMTP邮件指南
SMTP(Simple Mail Transfer Protocol)简单邮件传输协议,Python内置对SMTP的支持,可以发送纯文本文件,HTML邮件以及附带文件. 一.两个模块 Pyth ...
- VBScript学习第一天
编码工具:VbsEdit 1.MsgBox() 毫无例外,第一个要学的就是"Hello, World!" 直接输入: MsgBox ("Hello, World!&quo ...
- 使用ubuntu charmed kubernetes 部署一套生产环境的集群
官方文档: https://ubuntu.com/kubernetes/docs 搭建一个基本的集群 集群ip规划 hostname ip ubuntu-1 10.0.0.10 juju-contro ...
- Docker App应用
Docker App应用 这是一个实验特性. 实验性功能提供了对未来产品功能的早期访问.这些特性仅用于测试和反馈,因为它们可能在没有警告的情况下在不同版本之间更改,或者可以从将来的版本中完全删除.在生 ...
- GPU端到端目标检测YOLOV3全过程(下)
GPU端到端目标检测YOLOV3全过程(下) Ubuntu18.04系统下最新版GPU环境配置 安装显卡驱动 安装Cuda 10.0 安装cuDNN 1.安装显卡驱动 (1)这里采用的是PPA源的安装 ...
- vue3函数setUp和reactive函数详细讲解
1 setUp的执行时机 我们都知道,现在vue3是可以正常去使用methods的. 但是我们却不可以在setUp中去调用methods中的方法. 为什么了??? 我们先了解一下下面这两个生命周期函数 ...
- fiddler修改请求包和返回包
设置好过滤后,找到需要修改的包,按如下步骤进行包的数据修改1.设置"禁止上传"(禁止XX为本人自己理解,专业术语不记得了,高手可留言笔者重新修订博文),打上断点,如下标志就是在请求 ...