模块模式组织功能边界,命令模式封装操作,二者结合实现清晰灵活的撤销功能:按职责拆分剪辑系统为时间线、效果、音频、历史等模块,各模块提供明确接口;每个用户操作封装为含execute()和undo()的命令对象,接收模块实例并校验上下文;历史模块维护undo/redo双栈,限制大小并监听模块事件自动入栈;模块与命令严格解耦,所有变更通过命令驱动,确保可追溯可逆转。

模块模式负责组织功能边界,命令模式封装具体操作——两者结合能让撤销功能既清晰又灵活。关键在于把每个剪辑动作(如裁剪、加滤镜、调整音量)变成可执行、可回滚的命令对象,再由模块统一管理命令栈和状态同步。
模块划分:按职责拆分剪辑系统
将视频剪辑器拆为几个核心模块:时间线模块(管理轨道与片段)、效果模块(处理滤镜/转场)、音频模块(控制音量/静音)、历史模块(专管撤销重做)。每个模块对外只暴露明确接口,比如timeLine.addClip()或effects.applyFilter(),不直接操作DOM或全局状态。这样命令对象只需调用模块接口,解耦操作逻辑与UI渲染。
命令对象:封装动作+反向操作
每个用户操作都对应一个命令类,必须实现execute()和undo()方法。例如“删除片段”命令需记录被删片段的原始位置、ID和所属轨道,undo时调用时间线模块的insertClip()还原;“调节亮度”命令则保存调整前后的参数值,undo时传回旧值给效果模块。
- 命令构造时接收对应模块实例(如new DeleteClipCommand(timeLineModule, clipId)),避免硬编码依赖
- 执行命令前先调用canExecute()校验上下文(如片段是否存在、轨道是否锁定)
- 支持组合命令(CompositeCommand),用于批量操作(如“删除多个片段”)统一撤销
历史模块:管理命令栈与状态一致性
历史模块维护两个栈:undoStack(已执行命令)和redoStack(已撤销命令)。每次用户操作后,命令对象入栈并触发execute();点击撤销时弹出undoStack顶部命令并调用其undo(),结果推入redoStack。注意:模块内部状态变更(如时间线重排)必须通过命令触发,禁止绕过命令直接修改数据,否则undo会失效。
- 限制栈大小(如最多100步),超出时自动清除最早命令,防止内存溢出
- 监听关键模块事件(如timeLine.clipMoved),自动创建对应命令并入栈,覆盖用户拖拽等隐式操作
- 提供clear()方法清空历史栈(如新建项目时),同时通知各模块重置本地缓存
协同要点:模块与命令的交互约定
模块不主动记录历史,只响应命令调用;命令不直接操作数据,只驱动模块接口。例如添加字幕的操作流程是:用户点击“加字幕” → UI层创建AddSubtitleCommand(含文本、时间、样式)→ 命令调用subtitleModule.add() → 模块返回新字幕ID → 命令保存该ID用于undo → 历史模块将命令压入undoStack。整个过程无跨模块状态共享,所有变更可追溯、可逆转。
- 模块接口返回操作结果(如新增元素ID、修改前快照),供命令存储为undo依据
- 命令执行失败时(如格式不支持),立即抛出错误且不入栈,避免破坏历史一致性
- 导出/保存项目时,历史模块暂停记录,防止序列化干扰











