slate.js v0.58
op操作流程和slate协同编辑 eventEmitter版本 50行代码实现相同,只是op的消息流向变成
客户端->服务端->客户端s
依赖版本
返回文档
JSON
"express": "^4.17.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.3",
"slate": "^0.58.4",
"slate-react": "^0.58.4",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0"
协同编辑器
client
返回文档
JSX
import React, { useMemo, useState, useRef, useEffect } from 'react';
// 导入 Slate 编辑器的工厂函数。
import { createEditor } from 'slate'
// 导入 Slate 组件和 React 插件。
import { Slate, Editable, withReact } from 'slate-react';
import { initValue } from './slateInitialValue';
import io from 'socket.io-client';
const socket = io('http://localhost:3003');
const SocketSyncEditor = () => {
const editor = useMemo(() => withReact(createEditor()), [])
// 当设置 value 状态时,添加初始化值。
const [value, setValue] = useState(initValue)
const id = useRef(`${Date.now()}`)
const onChange = (value) => {
setValue(value)
const ops = editor.operations.filter( op => {
if(op) {
// 过滤选择,remote的op
return (op.type !== 'set_selection' && op.type !== 'set_node' && !op.remote)
}
return false;
})
// 有操作时才触发
if (ops.length) {
socket.emit('new-operations', {editorId: id.current, ops: JSON.stringify(ops)})
}
}
useEffect(() => {
socket.on('new-remote-operations', ({editorId, ops}) => {
// 触发的id和接收的id不等才有动作
if (id.current !== editorId) {
const parseOps = JSON.parse(ops);
for (let i = 0; i < parseOps.length; i++) {
const op = parseOps[i];
// 给op加个remote,防止当前editor更新apply了,又再次 emit 到其他编辑器,导致死循环
// 这里slate 0.47的版本apply是同步的,用一个变量就行
op.remote = true;
editor.apply(op);
}
}
})
}, [])
return (
<div className="slate-syncing-editor">
<Slate editor={editor} value={value} onChange={onChange}>
<Editable />
</Slate>
</div>
)
}
export default SocketSyncEditor;
server
返回文档
JavaScript
const app = require('express')()
const http = require('http').Server(app);
const io = require('socket.io')(http);
const port = 3003;
io.on('connection', (socket) => {
socket.on('new-operations', (data) => {
io.emit('new-remote-operations', data)
})
})
http.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})