现在,相关的基础知识我们应该有个大概的了解了,但离我们真正的开发出一个实用型的组件还有一段距离,不过不用担心,我们离目标已经越来越近。
以现在我们所了解的内容而言,或许你发现了一个问题,就是我们的编辑器的内容如何保存的问题,数据的保存是最重要的一个环节,无法保存的数据意义不大。我们以本地数据持久化为例来说明Slate中的这一相关功能。
到目前为止,我们的编辑一但在页面刷新的情况下就会还原到初始状态,即使我们做了诸多的内容编辑也会付诸东流。我们以本地存储为例,为<Slate/>
组件添加onChange
事件,如下所示:
SDocer.jsx
:
import { useState, useCallback } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';
import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';
function SDocer() {
const [editor] = useState(() => withReact(createEditor()));
return (
<Slate
editor={editor}
initialValue={initialValue}
onChange={value => {
const isAstChange = editor.operations.some(
op => 'set_selection' !== op.type
)
if (isAstChange) {
const content = JSON.stringify(value)
localStorage.setItem('content', content)
}
}}
>
<Editable
renderElement={useCallback(renderElement, [])}
renderLeaf={useCallback(renderLeaf, [])}
onKeyDown={event => {
if (!event.ctrlKey) return;
switch (event.key) {
case '`': {
event.preventDefault()
Helper.toggleCodeBlock(editor);
break
}
case 'b': {
Helper.toggleBoldMark(editor);
break
}
}
}}
/>
</Slate>
)
}
export default SDocer;
这时候当我们键入任何内容后在 localStorage
中 content
中的内容都能看到变化。如下所示:
虽然现在我们的内容能够实时的保存,但是页面一刷新还是还原了,这是显而易见的,因为我们并没有在组件初始化时从我们的LocalStore中读取数据,所以就只显示初始变量中的内容。我们调入localStorage中的内容就行了:
const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;
并把这个内容用useMemo
无依赖的静态化,如下所示:
import { useState, useCallback, useMemo } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';
import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';
const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;
function SDocer() {
const [editor] = useState(() => withReact(createEditor()));
return (
<Slate
editor={editor}
initialValue={useMemo(initDatas, [])}
onChange={value => {
const isAstChange = editor.operations.some(
op => 'set_selection' !== op.type
)
if (isAstChange) {
const content = JSON.stringify(value)
localStorage.setItem('content', content)
}
}}
>
<Editable
renderElement={useCallback(renderElement, [])}
renderLeaf={useCallback(renderLeaf, [])}
onKeyDown={event => {
if (!event.ctrlKey) return;
switch (event.key) {
case '`': {
event.preventDefault()
Helper.toggleCodeBlock(editor);
break
}
case 'b': {
Helper.toggleBoldMark(editor);
break
}
}
}}
/>
</Slate>
)
}
export default SDocer;
这个时候当你编辑后再刷新页面,内容就不在发生变化了。这样的json数据很适用,利于网络传输。但有时你可能特立独行,就是要走不一样的道路,也是可以的,我们可以自定义序列化 serialize
和 反序列化 deserialize
,比如我想保存一个纯文格式,或许就要这样做了:
新建一个工具文件 _untils.jsx
import { Node } from 'slate'
export const serialize = value => {
return (
value
.map(n => Node.string(n))
.join('\n')
)
}
export const deserialize = string => {
const content = string || ''
return content.split('\n').map(line => {
return {
children: [{ text: line }],
}
})
}
上面的工具很简单,就是把所有的节点纯文本化。以换行符分割。把上面的工具应用于Slate
,如下所示:
import { useState, useCallback, useMemo } from 'react';
import { createEditor, Editor, Transforms, Element } from 'slate';
import { Slate, withReact, Editable } from 'slate-react';
import { initialValue } from './_configure';
import { renderElement } from './_elementRender';
import { renderLeaf } from './_leafRender';
import { Helper } from './_helper';
import { serialize, deserialize } from './_untils';
// const initDatas = () => JSON.parse(localStorage.getItem('content')) || initialValue;
const initDatas = () => deserialize(localStorage.getItem('content')) || "";
function SDocer() {
const [editor] = useState(() => withReact(createEditor()));
return (
<Slate
editor={editor}
initialValue={useMemo(initDatas, [])}
onChange={value => {
const isAstChange = editor.operations.some(
op => 'set_selection' !== op.type
)
if (isAstChange) {
// const content = JSON.stringify(value)
localStorage.setItem('content', serialize(content))
}
}}
>
<Editable
renderElement={useCallback(renderElement, [])}
renderLeaf={useCallback(renderLeaf, [])}
onKeyDown={event => {
if (!event.ctrlKey) return;
switch (event.key) {
case '`': {
event.preventDefault()
Helper.toggleCodeBlock(editor);
break
}
case 'b': {
Helper.toggleBoldMark(editor);
break
}
}
}}
/>
</Slate>
)
}
export default SDocer;
结果符合预期。相似的做法,我也可以将内容序列化HTML、Markdown 等等,一切皆有可能。