[]
SpreadJS 协作系统通过协作客户端(前端)与协作服务端(后端)实现多人实时在线编辑。
本手册说明如何独立部署这两个组件,实现前端(静态网站)与后端(Node.js WebSocket 服务)的完全分离。
前后端分离部署,架构更灵活
SpreadJS 工作簿多用户实时同步
基于 Node.js 与 WebSocket 的可扩展后端服务
基于 SQLite 存储实现文档状态持久化
前端可使用任意 Web 服务器托管(Nginx、Apache、IIS 等)
项目包含两个独立目录:
collaboration-server/ # 纯后端服务 — 部署到 Node.js 环境
│── server.js
│── init-database.js
│── package.json
collaboration-client/ # 纯前端静态站点
│── public/
│ ├── index.html
│ ├── client.js
│ └── client.bundle.js # 由 webpack 打包生成
│── webpack.config.js
│── package.jsonmkdir collaboration-server && cd collaboration-server
npm init -y{
"name": "collaboration-server",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node ./server.js"
},
"dependencies": {}
}npm install @grapecity-software/js-collaboration @grapecity-software/js-collaboration-ot
npm install @grapecity-software/spread-sheets-collaboration
npm install sqlite3 @grapecity-software/js-collaboration-ot-sqliteimport http from 'http';
import sqlite3 from 'sqlite3';
import { Server } from '@grapecity-software/js-collaboration';
import * as OT from '@grapecity-software/js-collaboration-ot';
import { type } from '@grapecity-software/spread-sheets-collaboration';
import { SqliteDb } from '@grapecity-software/js-collaboration-ot-sqlite';
// 注册 SpreadJS 协作数据类型
OT.TypesManager.register(type);
const httpServer = http.createServer();
const server = new Server({ httpServer });
const port = 8080;
// 初始化 SQLite 数据库与适配器
const db = new sqlite3.Database("./docs.db");
const sqliteDbAdapter = new SqliteDb(db);
// 配置 OT 文档服务
const documentServices = new OT.DocumentServices({ db: sqliteDbAdapter });
server.useFeature(OT.documentFeature(documentServices));
// 启动服务
httpServer.listen(port, () => {
console.log(`协作服务正在监听端口 ${port}`);
});创建 init-database.js:
import sqlite3 from "sqlite3";
const db = new sqlite3.Database("./docs.db");
async function initSqliteDataTables(db) {
const run = (sql) => {
return new Promise((resolve, reject) => {
db.run(sql, (e) => (e ? reject(e) : resolve()));
});
};
await run(
`CREATE TABLE IF NOT EXISTS documents (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
version INTEGER NOT NULL,
snapshot_version INTEGER NOT NULL
)`
);
await run(
`CREATE TABLE IF NOT EXISTS operations (
doc_id TEXT NOT NULL,
version INTEGER NOT NULL,
operation TEXT NOT NULL,
PRIMARY KEY (doc_id, version),
FOREIGN KEY (doc_id) REFERENCES documents (id) ON DELETE CASCADE
)`
);
await run(
`CREATE TABLE IF NOT EXISTS snapshot_fragments (
doc_id TEXT NOT NULL,
fragment_id TEXT NOT NULL,
data TEXT NOT NULL,
PRIMARY KEY (doc_id, fragment_id),
FOREIGN KEY (doc_id) REFERENCES documents (id) ON DELETE CASCADE
)`
);
}
// 执行初始化
initSqliteDataTables(db);说明
仅在首次搭建或重置数据库时执行此步骤。
node init-database.js
mkdir collaboration-client && cd collaboration-client
npm init -y{
"name": "collaboration-client",
"version": "1.0.0",
"scripts": {
"build": "webpack"
},
"dependencies": {}
}# SpreadJS 及协作客户端包
npm install @grapecity-software/spread-sheets @grapecity-software/spread-sheets-collaboration-addon
npm install @grapecity-software/js-collaboration-client @grapecity-software/js-collaboration-ot-client
npm install @grapecity-software/spread-sheets-collaboration-client
# 构建工具
npm install --save-dev webpack webpack-cli style-loader css-loader创建并编写前端 HTML 页面:public/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SpreadJS 实时协作</title>
<script src="./client.bundle.js"></script>
</head>
<body>
<div id="ss" style="width:100vw; height:95vh; border:1px solid darkgray;"></div>
</body>
</html>创建并编写前端 JavaScript 代码:public/client.js
import * as GC from '@grapecity-software/spread-sheets';
import '@grapecity-software/spread-sheets-collaboration-addon';
import { Client } from "@grapecity-software/js-collaboration-client";
import * as OT from "@grapecity-software/js-collaboration-ot-client";
import { type, bind } from '@grapecity-software/spread-sheets-collaboration-client';
import "@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css";
// 注册 SpreadJS 协作数据类型
OT.TypesManager.register(type);
// ===== 重要:请修改为你的实际协作服务地址 =====
// 本地开发示例:
const SERVER_URL = "ws://127.0.0.1:8080";
// 生产环境示例:
// const SERVER_URL = "wss://collab.yourdomain.com";
// const SERVER_URL = "wss://your-app.herokuapp.com";
window.onload = async function () {
// 初始化 SpreadJS 工作簿
const workbook = new GC.Spread.Sheets.Workbook('ss');
// 建立客户端连接并加入协作房间
const conn = new Client(SERVER_URL).connect('room1');
const doc = new OT.SharedDoc(conn);
// 处理连接与文档同步错误
doc.on('error', (err) => console.error('协作异常:', err));
// 从服务端拉取文档状态
await doc.fetch();
if (!doc.type) {
// 创建新的共享文档并设置初始内容
workbook.getActiveSheet().getCell(0, 0).value("默认内容");
await doc.create(workbook.collaboration.toSnapshot(), type.uri, {});
// 绑定工作簿与共享文档,实现实时同步
bind(workbook, doc);
} else {
// 共享文档已存在,直接绑定
bind(workbook, doc);
}
};创建并配置前端项目打包文件:webpack.config.js
const path = require("path");
module.exports = {
entry: "./public/client.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "client.bundle.js",
},
mode: "development",
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};npm run build打包完成后,会在 public/ 目录下生成 client.bundle.js 文件。
将整个 public/ 目录上传至任意静态文件服务器(如 Nginx、Apache、IIS)。
功能 | 说明 |
|---|---|
实时同步 | 任一客户端的编辑操作会立即同步到同一文档房间的所有其他端 |
文档持久化 | 服务端使用 SQLite 存储文档快照与操作历史记录 |
前后端分离 | 前后端可独立部署、独立扩容 |
WebSocket 通信 | 轻量高效的实时连接方案 |
跨平台托管 | 支持任意静态 Web 托管环境与 Node.js 环境 |
cd collaboration-server
npm run start预期输出:协作服务正在监听端口 8080
将 collaboration-client/public/ 部署到静态服务器。
在多个浏览器或设备上打开前端页面
在其中一个端编辑任意单元格
观察所有已连接客户端会即时同步显示修改内容