使用状态快照在Vuex中撤消/重做

如何解决使用状态快照在Vuex中撤消/重做

上下文

我已经构建了一个可按以下步骤工作的撤消重做插件

已使用的步骤(概述)

  1. 在每个操作上添加以前的状态快照。
  2. 使用以prevIoUsstate作为参数的自定义replaceState方法撤消操作。
  3. 使用以前的状态作为参数,使用自定义的replaceState方法进行重做。

当我们处理一个巨大的嵌套状态时,当前正在处理性能问题。

性能问题

  • 我已经使用cloneDeep来制作状态的深层副本,因此在替换状态时不保留对当前状态的引用 状态,随着状态变大,cloneDeep需要更多时间 克隆。

  • 我使用了一个自定义的replaceState方法,该方法功能与replaceState完全相同,但是替换模块状态而不是整个应用程序的状态,这最终会花费更长的时间
    替换。

由于上述问题,撤消/重做变得迟钝且无法使用。

我很困。我在哪里错了,或者有更好的方法为复杂的状态应用程序实现撤消/重做?

代码

完成:用于撤消并存储状态快照。

撤消:用于重做和存储状态快照。

GlobalStore.js :所有模块组合在一起的全球商店。

export const store = new Vuex.Store({

     modules: { canvasEditor,dashboardMetrics }
     plugins: [ canvasEditorUndoRedo ]

})

下面是插件中的文件

index.js

import { Operations } from ‘./Operations’;

import { actionsToBeUsed } from './constants';

import { cloneDeep } from 'lodash';

export const op = new Operations();

export const canvasEditorUndoRedo = (store) => {

  /*

        Description: Perform undo/redo operation
        Steps:
        1.Initalize the class
        2.Store the prevIoUs state based on actions

  */
  op.init(store);

  store.subscribeAction((action,state) => {
    if (action.type != 'undo' &&action.type != 'redo' && actionsToBeUsed.find(actionType => actionType == action.type) != undefined) {
        // Store the state
        let stateClone = cloneDeep(state.canvasEditor);
        op.addSnapshot({ id: op.done.length + 1,action,state: stateClone });
    }
  });
}

Operation.js

import { cloneDeep } from 'lodash';
export class Operations {

  store
  done=[]
  undone = []

  init(store) {
    this.store = store;
  }

  addSnapshot(snapshot) {
    this.done.push(snapshot);
    this.updateOperationsCount();
  }

  clearUndo() {
    this.done = [];
    this.updateOperationsCount();
  }

  clearRedo() {
    this.undone = [];
    this.updateOperationsCount();
  }

  undo() {

    /*
    Description: Performs undo operation based on prevIoUsstate stored in array(done).
    Steps:

        I.    Get the last stored action from array(done)  pop it out.
        II.  Push the undo element(popped element) to redo’s an array (undone).
        III.   Replace the current state with stored state.

  */

    if (this.done.length != 0) {

      //I
      let undoELement = this.done.pop();

      //II
      this.undone.push({ id: undoELement.id,action: undoELement.action,state: this.store.state.canvasEditor});


      //III
      let state = cloneDeep(undoELement.state);
      this.replaceCanvasEditorState(state); 
      this.updateOperationsCount();

  }

  redo() {

    /*

    Description: Performs redo operation based on State stored in array(undone).
    Steps:
       I.   Get (pop) the last undo element from undone
      II.   Push the undo element(popped element) to undo’s an array (done) .
      III.   Replace the current state with stored state.
  */

    if (this.undone.length != 0) {

      //I
      let redoELement = this.undone.pop();

      //II
      this.done.push({ id: redoELement.id,action: redoELement.action,state: this.store.state.canvasEditor });

      //III
      let state = cloneDeep(redoELement.state);
      this.replaceCanvasEditorState(state);
      this.updateOperationsCount();
    }
  }

  replaceCanvasEditorState(state) {
  /*
     Description: Replaces current state with state provided as parameter.
  */
    this.store._withCommit(() => {
      this.store.state.canvasEditor = state;
    });

  }

  updateOperationsCount() {

    let undoRedo = {
      doneLength: this.done.length,undoneLength: this.undone.length
    }
    this.store.commit('updateUndoRedoCount',undoRedo);

  }

}

CodeSandBox

该应用程序处理许多复杂的操作。

解决方法

恐怕使用快照(深度克隆)在巨大的嵌套状态上执行撤消/重做总是很慢的,更不用说内存消耗了

因此,最好的选择可能是彻底改变策略。当您使用Vuex时,必须使用突变来完成所有更改,因此使用动作/逆向动作策略来实现它应该不难。在每个突变内,不要将状态快照推送到堆栈上,而应在堆栈上推送“反向操作”突变(使用旧的子状态作为参数-如果是对象,则为deepClone d)。

确保它不像您最初的方法那样透明,并且要求变异作者以特定的方式编写变异,但是您始终可以编写一些帮助程序来使其变得更加容易和“标准化” ...

或者您可以看看undo-redo-vuex Vuex插件,该插件通过保存初始状态和执行的所有变异来实现撤消/重做。当需要撤消时,它将状态重置为初始状态,并重播除最后一个突变以外的所有突变。不知道它的扩展性如何,但至少对突变作者更透明...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?