Draft.js 基于 immutable.js 实现了一种不可变的数据结构,基于这种数据结构, Draft.js 实现了单项数据流,即 event ->onChange ->EditorState
我在 CodePen 上面实现了一个简单的Demo用于展示EditorState,你可以在上面尝试一下。
上图展示了一个完整的EditorState,我们可以轻易的猜测出一些属性的含义,比如currentContent记录了当前编辑器中展示内容的状态,selection中标识当前的选区,redoStack和undoStack分别是撤销、重做栈,而EditorState则提供了操作这些属性的顶层方法。
Draft 将样式分为块级样式与内联样式,块级样式之间线性排列,不可嵌套,内联样式则用数组记录,可以重叠。
当我在 draft 中随意输入一些文字的时候,text将会记录下文字内容,而此时用于记录块级样式的type值为 unstyled
现在我们将H1(块级元素)应用于文本,我们可以发现用于标识块级样式的type值变为了 header-one ,当我们继续在该行输入的时候,将会同样被赋予H1样式。通过上述的数据结构我们可以发现,由于块级样式由type所规定,所以块级样式之间不可被嵌套,即不会出现无序列表嵌套有序列表的情况出现。
对于同一块级元素中的文本内容, draft 将其按照字符进行拆分,每一个字符的内联样式用数组进行展示,由此可以解决内联样式的嵌套问题。
Entity并不是一个难以理解的数据类型,Entity数据主要用于展示一些不可被分割的数据类型,例如图片、mention、超链接等。
典型场景就是mention类型,详细来说,我们在日常使用中会在富文本编辑器中提及一些人, @xxx 的数据类型要求是一个统一整体,一旦 @xxx 中有某一个字符被删除,就有可能会出现 @ 出的名字和 @ 的对象不一致的情况。
在上图中,我将插入的图片数据结构打印了出来,我们可以发现这是一个Entity,记录了data,mutability与type, Draft.js 向外暴露了一个顶层接口,当Entity数据被注入的时候,可以通过这个接口来设计数据对象的展现方式,如下图
一、树结构定义一颗树,JS中常见的树形数据结构如下,children属性对应的是子树
let tree = [
{
id: '1',
name: '节点1',
children: [
{
id: '1-1',
name: '节点1-1'
}
]
},
{
id: '2',
name: '节点2',
children: [
{
id: '2-1',
name: '节点2-1'
},
{
id: '2-2',
name: '节点2-2',
children: [
{
id: '2-2-1',
name: '节点2-2-1'
}
]
}
]
},
{
id: '3',
name: '节点3'
}
]
二、深度优先遍历(DFS)
1、递归实现
function treeIterator(tree, func) {
tree.forEach((node) =>{
func(node)
node.children &&treeIterator(node.children, func)
})
}
实现逻辑简述:定义treeIterator函数,传入tree(树)和func(回调函数)两个参数,遍历tree数组,执行回调函数,如果当前节点存在children,则递归调用。
函数调用验证:调用treeIterator函数,传入上文定义好的树结构数组,打印出每个节点的name值。
treeIterator(tree, (node) =>{
console.log(node.name)
})
控制台打印结果如下:
2、循环实现
function treeIterator(tree, func) {
let node, curTree = [...tree]
while ((node = curTree.shift())) {
func(node)
node.children &&curTree.unshift(...node.children)
}
}
实现逻辑简述:
(1)定义node作为当前节点,curTree为传入的树(不影响原数组tree);
(2)执行while循环,curTree数组第一个元素从其中删除,并返回第一个元素赋值给node;
(3)①执行回调函数;②如果当前节点存在子树,则追加到curTree数组的开头,继续执行循环,直到curTree没有元素为止。
函数调用验证:参考上述递归实现验证,方式和结果一致。
三、广度优先遍历(BFS)
function treeIterator(tree, func) {
let node, curTree = [...tree]
while ((node = curTree.shift())) {
func(node)
node.children &&curTree.push(...node.children)
}
}
实现逻辑简述:和上述深度优先遍历的循环实现差不多。区别在于如果当前节点存在子树,则追加到list数组的末尾。