使用LiveBlocks构建React+Redux实时协作白板应用
前言
在现代Web应用中,实时协作功能变得越来越重要。本文将介绍如何利用LiveBlocks这一强大的实时协作框架,结合React和Redux,构建一个功能完善的在线协作白板应用。通过本教程,你将掌握实时数据同步、状态管理和多人协作的核心技术要点。
技术栈概述
本项目采用以下技术组合:
- React:用于构建用户界面
- Redux:状态管理工具
- LiveBlocks:提供实时协作能力
- 实时数据同步
- 用户状态管理
- 历史记录功能
项目初始化与配置
1. 创建项目基础结构
首先创建一个标准的React应用:
npx create-react-app redux-whiteboard
安装必要的依赖包:
npm install redux react-redux @reduxjs/toolkit @liveblocks/client @liveblocks/redux
2. 配置LiveBlocks客户端
在src/store.js
中初始化LiveBlocks客户端:
import { createClient } from "@liveblocks/client";
import { liveblocksEnhancer } from "@liveblocks/redux";
import { configureStore, createSlice } from "@reduxjs/toolkit";
export const client = createClient({
publicApiKey: "你的公开API密钥",
});
3. 设置Redux Store
配置Redux store并集成LiveBlocks增强器:
const initialState = {
shapes: {},
selectedShape: null,
};
const slice = createSlice({
name: "state",
initialState,
reducers: {
// 后续添加reducer逻辑
},
});
export function makeStore() {
return configureStore({
reducer: slice.reducer,
enhancers: [
liveblocksEnhancer({
client,
storageMapping: { shapes: true },
presenceMapping: { selectedShape: true },
}),
],
});
}
核心功能实现
1. 房间连接管理
LiveBlocks使用"房间"的概念来隔离不同的协作空间。我们需要在组件挂载时连接房间,卸载时断开连接:
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { actions } from "@liveblocks/redux";
const roomId = "redux-whiteboard";
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.enterRoom(roomId));
return () => dispatch(actions.leaveRoom());
}, [dispatch]);
// 组件其余部分
}
2. 形状管理
添加矩形
实现添加随机位置和颜色的矩形功能:
const COLORS = ["#DC2626", "#D97706", "#059669", "#7C3AED", "#DB2777"];
const slice = createSlice({
// ...其他配置
reducers: {
insertRectangle: (state) => {
const shapeId = Date.now().toString();
const shape = {
x: Math.floor(Math.random() * 300),
y: Math.floor(Math.random() * 300),
fill: COLORS[Math.floor(Math.random() * COLORS.length)],
};
state.shapes[shapeId] = shape;
state.selectedShape = shapeId;
},
},
});
删除矩形
实现删除选中矩形的功能:
deleteShape: (state) => {
if (state.selectedShape) {
delete state.shapes[state.selectedShape];
state.selectedShape = null;
}
},
3. 选择与移动功能
选择矩形
通过用户presence同步选择状态:
onShapePointerDown: (state, action) => {
state.selectedShape = action.payload;
},
移动矩形
实现矩形拖拽移动功能:
onCanvasPointerMove: (state, action) => {
if (state.selectedShape && state.shapes[state.selectedShape]) {
state.shapes[state.selectedShape].x += action.payload.dx;
state.shapes[state.selectedShape].y += action.payload.dy;
}
},
4. 用户界面组件
矩形组件
const Rectangle = ({ shape, selectionColor, id }) => {
const dispatch = useDispatch();
return (
<div
className="rectangle"
style={{
transform: `translate(${shape.x}px, ${shape.y}px)`,
backgroundColor: shape.fill || "#CCC",
borderColor: selectionColor,
}}
onPointerDown={(e) => {
e.stopPropagation();
dispatch(onShapePointerDown(id));
}}
/>
);
};
主应用组件
function App() {
const shapes = useSelector((state) => state.shapes);
const isLoading = useSelector((state) => state.liveblocks.isStorageLoading);
const others = useSelector((state) => state.liveblocks.others);
const selectedShape = useSelector((state) => state.selectedShape);
const dispatch = useDispatch();
// 处理拖拽移动
const handlePointerMove = (e) => {
if (selectedShape) {
dispatch(
onCanvasPointerMove({
dx: e.movementX,
dy: e.movementY,
})
);
}
};
return (
<>
<div className="canvas" onPointerMove={handlePointerMove}>
{Object.entries(shapes).map(([shapeId, shape]) => {
let selectionColor = "transparent";
if (selectedShape === shapeId) {
selectionColor = "blue";
} else if (
others.some((user) => user.presence?.selectedShape === shapeId)
) {
selectionColor = "green";
}
return (
<Rectangle
key={shapeId}
id={shapeId}
shape={shape}
selectionColor={selectionColor}
/>
);
})}
</div>
<div className="toolbar">
<button onClick={() => dispatch(insertRectangle())}>添加矩形</button>
<button
onClick={() => dispatch(deleteShape())}
disabled={selectedShape == null}
>
删除
</button>
</div>
</>
);
}
进阶功能扩展
1. 撤销/重做功能
LiveBlocks内置支持操作历史记录,可以轻松实现撤销/重做功能:
import { useUndo, useRedo } from "@liveblocks/react";
function Toolbar() {
const undo = useUndo();
const redo = useRedo();
return (
<div className="toolbar">
{/* 其他按钮 */}
<button onClick={undo}>撤销</button>
<button onClick={redo}>重做</button>
</div>
);
}
2. 用户光标显示
可以通过LiveBlocks的presence功能显示其他用户的光标位置:
function OtherCursors() {
const others = useOthers();
return others.map(({ connectionId, presence }) => {
if (!presence?.cursor) return null;
return (
<div
key={connectionId}
className="cursor"
style={{
transform: `translate(${presence.cursor.x}px, ${presence.cursor.y}px)`,
}}
/>
);
});
}
性能优化建议
- 选择性渲染:使用React.memo优化矩形组件,避免不必要的重绘
- 批量更新:对于频繁的移动操作,考虑使用防抖或节流
- 数据分片:对于大型白板,考虑将画布分区加载
- 离线支持:利用LiveBlocks的离线缓存功能提升用户体验
总结
通过本教程,我们实现了一个功能完整的实时协作白板应用,包含以下核心功能:
- 实时添加/删除矩形
- 多人协作选择与移动
- 状态持久化
- 操作历史记录
LiveBlocks与Redux的结合为构建复杂的协作应用提供了强大而灵活的解决方案。开发者可以基于此基础进一步扩展功能,如添加更多形状类型、实现文本编辑或引入更复杂的协作场景。
这种架构模式不仅适用于白板应用,也可以应用于任何需要实时协作功能的Web应用,如文档编辑器、项目管理工具或设计协作平台等。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考