現在,相關的基礎知識我們應該有個大概的了解了,但離我們真正的開發出一個實用型的組件還有一段距離,不過不用擔心,我們離目標已經越來越近。
以現在我們所了解的內容而言,或許你發現了一個問題,就是我們的編輯器的內容如何保存的問題,數據的保存是最重要的一個環節,無法保存的數據意義不大。我們以本地數據持久化為例來說明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 等等,一切皆有可能。