序文
ローコード プロジェクトで発生する元に戻すおよびやり直し機能は、vue3 に基づいて実装されています。
実装手順
- pinia を使用して、履歴レコード配列 (arr)、現在のページに表示される対応するインデックス (index)、およびスナップショット (isSnapshot) を今すぐ生成するかどうかを保存します。時間が経過すると、ウォッチ内のメソッドと最大データ数がトリガーされます。を保存することができます (maxStep)。pinia ではいくつかのメソッドが定義されています。
- 最初のメソッドはスナップショットを生成し、ページ要素配列の変更を監視し、変更があったときにメソッドを呼び出し、メソッドに入った後にスナップショットを生成できるかどうかを判断します。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;
}
- 2 つ目はオーバーフローかどうかを判断し、オーバーフローによって要素がシフトアウトされます。
// 判断是否溢出
isFull() {
if (this.snapshotData.length == this.maxStep) {
this.snapshotData.shift(0);
}
},
- 3 番目の取り消しは、index>0 の場合、すぐに取り消すことができることを証明し、isSnapshot=false を設定し、index を 1 ずつ減分して、index が指す要素配列をページにレンダリングします。
// 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];
}
}
},
- 4 番目のやり直しでは、配列の最後がやり直し可能であることが証明されない場合、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];
}
}
}
注意点
シンプルで便利ですが、データ損失が発生する可能性があります。
欠点:
- JSON.Stringify によって変換されたデータに、関数、未定義、シンボルなどの列挙不可能なプロパティが含まれている場合、キーと値のペアは JSON.Stringify のシリアル化後に消えます。
- 変換されたデータには NaN および Infinity 値 (-Infinity を含む) が含まれており、JSON シリアル化後の結果は null になります。
- 変換されたデータには Date オブジェクトが含まれており、JSON.Stringify シリアル化後に文字列になります。
- RegExp 参照型を含む変換されたデータは、シリアル化後に空のオブジェクトになります。
- 列挙不可能なプロパティをシリアル化できません。
- オブジェクトへの循環参照はシリアル化できません (例: obj[key] = obj)。
- オブジェクトのプロトタイプ チェーンをシリアル化できませんでした。
コード全体
// 监听事件,这里会遇到频繁触动的问题,用防抖解决
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];
}
}
}
}
})