最近在实现互动白板 undo
、redo
功能时候了解到了命令模式,直接上代码,其实并不复杂
可以看到 UndoRedoManager 主要就是维护了执行栈和未执行栈,然后每次 undo
、redo
时候入栈出栈执行对应 cmd 的 execute
或 unexecute
方法
function Count() {
const manager = useMemo(() => new UndoRedoManager(), [])
const [pos, setPos] = useState({ x: 10, y: 10 })
function move() {
const oldPos = pos
const newPos = { x: Math.random() * 150, y: Math.random() * 150 }
const cmd = {
execute: () => {
setPos(newPos)
},
unexecute: () => {
setPos(oldPos)
},
}
manager.execute(cmd)
}
function undo() {
manager.undo()
}
function redo() {
manager.redo()
}
return (
<>
<div style={{ position: 'relative', border: '1px solid royalblue', width: 200, height: 200 }}>
<div
style={{
position: 'absolute',
left: pos.x,
top: pos.y,
width: 50,
height: 50,
background: 'royalblue',
transition: '.2s',
}}
/>
</div>
<br />
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>
<button onClick={move}>move</button>
<button onClick={undo}>undo</button>
<button onClick={redo}>redo</button>
</div>
</>
)
}
所谓命令模式实现的 undo
、redo
功能其实就是当你想执行一个动作时候,同时提供该命令的反向操作,以上边 demo 为例,当你想把方块从 (0, 0) 移动到 (100, 100) 时候,需同时提供一个反向命令把它从 (100, 100) 移动到 (0, 0),命令是在 manager 中帮你执行,可以做到代码抽象分离,避免业务代码中掺合过多控制流程