vue3에서 실행 취소 및 다시 실행

머리말

로우코드 프로젝트에서 마주치는 실행 취소 및 다시 실행 기능은 vue3을 기반으로 구현됩니다.

구현 단계

  1. pinia를 사용하여 기록 레코드 배열(arr), 현재 페이지에 표시되는 해당 인덱스(index) 및 지금 스냅샷 생성 여부(isSnapshot)를 저장합니다. 시간은 시계에서 메서드를 트리거하고 데이터의 최대 수를 트리거합니다. 저장할 수 있습니다(maxStep). 여러 방법이 pinia에 정의되어 있습니다.
  2. 첫 번째는 스냅샷을 생성하고, 페이지 요소 배열의 변경 사항을 모니터링하고, 변경될 때 메서드를 호출하고, 메서드를 입력한 후 스냅샷을 생성할 수 있는지 여부를 판단합니다. isSnapshot=true로 종료할 수 없습니다. 먼저 색인이 대기열 끝에 있는지 여부를 판단합니다. 더 이상 색인 뒤의 요소를 삭제할 필요가 없으며 페이지 요소 배열을 딥 복사하여 기록 레코드 배열로 푸시합니다.
	addSnapshot() {
    
    
          if (this.isSnapshot) {
    
    
              this.isFull();//判断是否溢出 溢出删除一个
              let n = this.snapshotData.length;
              // 当前索引不在开头需要恢复时
              if (this.curIndex < n-1) {
    
    
                  this.snapshotData.splice(this.curIndex+1);
              }
              // elStore().els为页面元素数组
              this.snapshotData.push(JSON.stringify(elStore().els));
              this.curIndex++;
          }
          this.isSnapshot = true;
      }
  1. 두 번째는 오버플로 여부를 판단하고 오버플로는 요소를 이동합니다.
        // 判断是否溢出
        isFull() {
    
    
            if (this.snapshotData.length == this.maxStep) {
    
    
                this.snapshotData.shift(0);
            }
        },
  1. 세 번째 취소는 index>0인 경우 지금 취소할 수 있음을 증명한 다음 isSnapshot=false를 설정한 다음 인덱스를 1씩 감소시키고 인덱스가 가리키는 요소 배열을 페이지에 렌더링합니다.
		// undo撤销
        undo() {
    
    
            if (this.curIndex > 0) {
    
    
                this.isSnapshot = false;
                let t = JSON.parse(this.snapshotData[--this.curIndex]);
                for (let i = 0; i < t.length; i++) {
    
    
                    elStore().els[i] = t[i];
                }
            }
        },
  1. 네 번째 redo는 배열의 끝이 redo 가능하지 않은 경우 isSnapshot=false로 설정한 다음 인덱스에 1을 추가하고 인덱스가 가리키는 요소 배열을 페이지에 렌더링합니다.
        // record 恢复
        record() {
    
    
            // 判断是不是到尾部,有才可以恢复 
            if (this.curIndex < this.snapshotData.length-1) {
    
    
                this.isSnapshot = false;
                let t = JSON.parse(this.snapshotData[++this.curIndex]);
                for (let i = 0; i < t.length; i++) {
    
    
                    elStore().els[i] = t[i];
                }
            }
        }

주의점

간단하고 편리하지만 데이터 손실이 발생할 수 있습니다.
결점:

  1. JSON.Stringify로 변환된 데이터에 function, undefined 및 Symbol과 같은 열거 불가능한 속성이 포함되어 있으면 JSON.Stringify 직렬화 후에 키-값 쌍이 사라집니다.
  2. 변환된 데이터에는 NaN 및 Infinity 값(-Infinity 포함)이 포함되며 JSON 직렬화 이후의 결과는 null이 됩니다.
  3. 변환된 데이터에는 JSON.Stringify 직렬화 후 문자열이 되는 Date 개체가 포함되어 있습니다.
  4. RegExp 참조 유형을 포함하는 변환된 데이터는 직렬화 후에 빈 개체가 됩니다.
  5. 열거 불가능한 속성을 직렬화할 수 없습니다.
  6. 개체에 대한 순환 참조는 직렬화할 수 없습니다(예: obj[key] = obj).
  7. 개체의 프로토타입 체인을 직렬화할 수 없습니다.

전체 코드

// 监听事件,这里会遇到频繁触动的问题,用防抖解决
    watch(useElStore.els, debounce(() => {
    
    
      snapshotStore.addSnapshot();
    }, 400), {
    
     immediate: true });
    
 // 撤销
    function cancel() {
    
    
      changeRectShow();
      snapshotStore.undo();
    }

    // 重做
    function record() {
    
    
      changeRectShow();
      snapshotStore.record();
    }
// 防抖代码
export default (fn,delay) =>{
    
    
    let t = null;
    return () => {
    
    
        if (t != null) {
    
    
            clearInterval(t);
        }
        t = setTimeout(() => {
    
    
            fn();
        },delay)
    }
}
// snapshot仓库
import {
    
     defineStore } from 'pinia'
import elStore from './index'
export default defineStore('snapshot', {
    
    
    state() {
    
    
        return {
    
    
            snapshotData: [],// 快照数据
            maxStep: 30,// 最大能够存储的数据数
            curIndex: -1,// 当前所在下标
            isSnapshot: true,// 是否可以保存
        }
    },
    actions: {
    
    
        // 添加快照
        addSnapshot() {
    
    
            if (this.isSnapshot) {
    
    
                this.isFull();//判断是否溢出 溢出删除一个
                let n = this.snapshotData.length;
                // 当前索引不在尾部,说明进行了撤销,需要删除后面的元素
                if (this.curIndex < n-1) {
    
    
                    this.snapshotData.splice(this.curIndex+1);
                }
                this.snapshotData.push(JSON.stringify(elStore().els));
                this.curIndex++;
            }
            this.isSnapshot = true;
        },
        // 判断是否溢出
        isFull() {
    
    
            if (this.snapshotData.length == this.maxStep) {
    
    
                this.snapshotData.shift(0);
            }
        },
        // undo撤销
        undo() {
    
    
            if (this.curIndex > 0) {
    
    
                this.isSnapshot = false;
                let t = JSON.parse(this.snapshotData[--this.curIndex]);
                for (let i = 0; i < t.length; i++) {
    
    
                    elStore().els[i] = t[i];
                }
            }
        },
        // record 恢复
        record() {
    
    
            // 判断是不是到尾部,有才可以恢复 
            if (this.curIndex < this.snapshotData.length-1) {
    
    
                this.isSnapshot = false;
                let t = JSON.parse(this.snapshotData[++this.curIndex]);
                for (let i = 0; i < t.length; i++) {
    
    
                    elStore().els[i] = t[i];
                }
            }
        }
    }
})

추천

출처blog.csdn.net/m0_53062835/article/details/127789240