现在,相关的基础知识我们应该有个大概的了解了,但离我们真正的开发出一个实用型的组件还有一段距离,不过不用担心,我们离目标已经越来越近。
以现在我们所了解的内容而言,或许你发现了一个问题,就是我们的编辑器的内容如何保存的问题,数据的保存是最重要的一个环节,无法保存的数据意义不大。我们以本地数据持久化为例来说明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 (<Slateeditor={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)}}}><EditablerenderElement={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 (<Slateeditor={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)}}}><EditablerenderElement={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 (<Slateeditor={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))}}}><EditablerenderElement={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 等等,一切皆有可能。