slate.js v0.58

依赖版本

返回文档
JSON

  1. "mitt": "^2.1.0",
  1. "react": "^16.13.1",
  1. "react-dom": "^16.13.1",
  1. "react-scripts": "3.4.3",
  1. "slate": "^0.58.4",
  1. "slate-react": "^0.58.4"

协同编辑器

返回文档
JSX

  1. import React, { useMemo, useState, useRef, useEffect } from 'react';
  1. // eventEmitter库
  1. import Mitt from 'mitt';
  1. // 导入 Slate 编辑器的工厂函数。
  1. import { createEditor } from 'slate'
  1. // 导入 Slate 组件和 React 插件。
  1. import { Slate, Editable, withReact } from 'slate-react';
  1. // 初始值
  1. const initValue = [{ type: 'paragraph',children: [{ text: 'slate协同编辑 eventEmitter版本 50行代码实现'}]}]
  1. // eventEmitter实例
  1. const emitter = new Mitt();
  1. const SyncEditor = () => {
  1. const editor = useMemo(() => withReact(createEditor()), [])
  1. // 当设置 value 状态时,添加初始化值。
  1. const [value, setValue] = useState(initValue)
  1. // 确定唯一编辑器ID
  1. const id = useRef(`${Date.now()}`)
  1. const onChange = (value) => {
  1. setValue(value)
  1. const ops = editor.operations.filter( op => {
  1. if(op) {
  1. // 过滤 operation,remote的op
  1. return (op.type !== 'set_selection' && op.type !== 'set_node' && !op.remote)
  1. }
  1. return false;
  1. })
  1. // 有操作时才触发
  1. if (ops.length) {
  1. emitter.emit(id.current, ops)
  1. }
  1. }
  1. useEffect(() => {
  1. emitter.on('*', (type, ops) => {
  1. // 触发的id和接收的id不等才有动作
  1. if (id.current !== type) {
  1. for (let i = 0; i < ops.length; i++) {
  1. const op = ops[i];
  1. // 给op加个remote,防止当前editor更新apply了,又再次 emit 到其他编辑器,导致死循环
  1. // 这里slate 0.47的版本apply是同步的,用一个变量就行
  1. op.remote = true;
  1. // 这里也可以用Commands,不过不同命令太多了
  1. editor.apply(op);
  1. }
  1. }
  1. })
  1. }, [])
  1. return (
  1. <div className="slate-syncing-editor">
  1. <Slate editor={editor} value={value} onChange={onChange}>
  1. <Editable />
  1. </Slate>
  1. </div>
  1. )
  1. }
  1. export default SyncEditor;

使用协同编辑器

返回文档
JSX

  1. import React from 'react';
  1. import SyncingEditor from './syncEditor';
  1. function App() {
  1. return (
  1. <div className="App">
  1. <SyncingEditor />
  1. <SyncingEditor />
  1. <SyncingEditor />
  1. </div>
  1. );
  1. }
  1. export default App;

示例

返回文档
image.pngTab-1599212217181.webm正在转码中,请稍候…

Play Pause

Fullscreen Exit fullscreen

00:00 00:00

  • 2x
  • 1.5x
  • 1.25x
  • 1x
  • 0.75x
  • 0.5x

1x

  1. Replay 请刷新试试

源码

返回文档slate-syncing.zip(446.66KB)

slate.js v0.47

依赖版本

返回文档
JSON

  1. "immutable": "^4.0.0-rc.12",
  1. "mitt": "^2.1.0",
  1. "react": "^16.13.1",
  1. "react-dom": "^16.13.1",
  1. "slate": "^0.47.3",
  1. "slate-react": "^0.22.3",

协同编辑器

返回文档
JSX

  1. import React, { useState, useRef, useEffect } from "react";
  1. import { Editor } from "slate-react";
  1. import Mitt from "mitt";
  1. import { Operation, Value } from "slate";
  1. const initValue = Value.fromJSON({
  1. document: {
  1. nodes: [{
  1. object: "block",
  1. type: "paragraph",
  1. nodes: [
  1. {
  1. object: "text",
  1. leaves: [
  1. {
  1. text: "slate 0.47协同编辑 eventEmitter版本 50行代码实现"
  1. }
  1. ]
  1. }
  1. ]
  1. }
  1. ]}
  1. });
  1. const emitter = new Mitt();
  1. export const SyncingEditor = () => {
  1. const [value, setValue] = useState(initialValue);
  1. const id = useRef(`${Date.now()}`);
  1. const editor = useRef(null);
  1. const remote = useRef(false);
  1. useEffect(() => {
  1. emitter.on("*", (type, ops) => {
  1. if (id.current !== type) {
  1. // 0.47的applyOperation是同步的
  1. remote.current = true;
  1. ops.forEach(op => editor.current!.applyOperation(op));
  1. remote.current = false;
  1. }
  1. });
  1. }, []);
  1. return (
  1. <Editor ref={editor} value={value} onChange={opts => {
  1. setValue(opts.value);
  1. const ops = opts.operations.filter(op => {
  1. if (op) {
  1. return (
  1. o.type !== "set_selection" &&
  1. o.type !== "set_value" &&
  1. (!o.data || !o.data.has("source"))
  1. );
  1. }
  1. return false;
  1. })
  1. .toJS()
  1. .map((o: any) => ({ ...o, data: { source: "one" } }));
  1. if (ops.length && !remote.current) {
  1. emitter.emit(id.current, ops);
  1. }
  1. }}
  1. />
  1. );
  1. };