vuex實現歷史記錄的示例程式碼
阿新 • • 發佈:2021-05-13
最近自研著一個視覺化操作平臺,其中涉及到使用者操作後可撤銷或重做,在網上搜了一些解決思路,完善自己所設想的解決思路。
歷史記錄需求的要點
- 可儲存在 localStorage 中
- 可多次撤銷或多次重做
- 點選列表中的一項,將歷史倒退或前進至指定位置
看似簡單的需求,在基礎建設設計上的錯誤,亦會在未來導致更多的工作量。所以結合上面兩點的要求,發現 vuex 的基本思路非常適合完成這個需求,redux 同樣。
實現思路
此專案用了 typescript 來加強程式碼的嚴謹性,方便日後維護,大家簡單看個思路。
1. 先定義歷史記錄的資料結構
interface HistoryItem { timestrap: number; // 記錄時間戳 name: string; // 記錄名稱 redo: string; // 重做Mutation undo: string; // 撤銷Mutation redoParams: any[]; // 重做Mutation提交引數 undoParams: any[]; // 撤銷Mutation提交引數 } interface HistoryStatus { historys: HistoryItem[]; // 記錄history陣列 _currentHistory: number; // 當前節點索引 }
2. 編寫 History 狀態模組
編寫基礎操作history狀態的vuex module,建立記錄的Mutation,重做和撤銷的Action
一條記錄是包含對這個步驟的執行redo操作與撤銷undo操作的。所以在使用者點選列表其中一項時,應該是迴圈回退到當前項的前一項undo,或迴圈redo到當前項
所以需要增加一條空記錄,方便使用者點選空記錄撤銷最初的操作。
運用了vuex-module-decorators 裝飾器,寫更易維護的程式碼
import { VuexModule,Module,Mutation,Action } from "vuex-module-decorators"; @Module({ namespaced: true }) export class HistoryModule extends VuexModule<HistoryStatus> implements HistoryStatus { /** * 初始化一個空記錄的原因主要是方便列表操作時: * 當用戶點選最早的一條記錄時,可以正常撤銷使用者操作的第一步 **/ public historys: HistoryItem[] = [ { name: `開啟`,timestrap: Date.now(),redo: "",redoParams: [],undo: "",undoParams: [],},]; public _currentHistory: number = 0; // getter get current(){ return this._currentHistory; } // getter get historyList(): HistoryItem[] { return this.historys || []; } // 建立歷史記錄 @Mutation public CREATE_HISTORY(payload: HistoryItem) { if (this._currentHistory < this.historys.length - 1) { this.historys = this.historys.slice(0,this._currentHistory); } // 由於js的深淺拷貝問題,所以在建立時都需要對資料進行深拷貝 // 想嘗試lodash的clone函式,但發現好像JSON.stringify的方式clone應該更快的,畢竟我們的資料不存在函式 // 我這裡就先不改了,主要是表達出思路即可 this.historys.push(_.cloneDeep(payload)); this._currentHistory = this.historys.length - 1; } @Mutation public SET_CURRENT_HISTORY(index: number) { this._currentHistory = index < 0 ? 0 : index; } // 重做 @Action public RedoHistory(times: number = 1) { let { state,commit } = this.context; let historys: HistoryItem[] = state.historys; let current: number = state._currentHistory; if (current + times >= historys.length) return; while (times > 0) { current++; let history = historys[current]; if (history) { commit(history.redo,...history.redoParams,{ root: true }); } times--; } commit("SET_CURRENT_HISTORY",current); } // 撤銷 @Action public UndoHistory(times: number = 1) { let { state,commit } = this.context; let historys: HistoryItem[] = state.historys; let current: number = state._currentHistory; if (current - times < 0) return; while (times > 0) { le程式設計客棧t history = historys[current]; if (history) { commit(history.undo,...history.undoParams,{ root: true }); } times--; curreqlcvgNnt--; } commit("SET_CURRENT_HISTORY",current); } }
3. 編寫可以撤銷或重做的功能
完成上面兩步後,我們就可以編寫各種操作了
編寫對資料基礎操作的Mutation
@Mutation
public CREATE_PAGE(payload: { page: PageItem; index: number }) {
this.pages.splice(payload.index,_.cloneDeep(payload.page));
this._currentPage = this.pages.length - 1;
}
@Mutation
public REMOVE_PAGE(id: string) {
let index = this.pages.findIndex((p) => p.id == id);
index > -1 && this.pages.splice(index,1);
www.cppcns.com if (this._currentPage == index) {
this._currentPage = this.pages.length > 0 ?http://www.cppcns.com 0 : -1;
}
}
將基礎操作按要求封裝成帶儲存->記錄->執行的Action
// 包裝建立頁面函式 @Action public CreatePage(type: "page" | "dialog") { let { state,commit } = this.context; // 記錄儲存即將建立的頁面 let id = _.uniqueId(type) + Date.now(); let pageName = pageType[type]; let page: PageItem = { id,name: `${pageName}${state.pages.length + 1}`,type,layers: [],style: { width: 720,height: 1280 },}; //建立歷史記錄 let history: HistoryItem = { name: `建立${pageName}`,redo: "Page/CREATE_PAGE",redoParams: [{ index: state.pages.length - 1,page }],undo: "Page/REMOVE_PAGE",undoParams: [id],}; // 儲存記錄此歷史記錄 commit("Histroy/CREATE_HISTORY",history,{ root: true }); commit(history.redo,{ root: true }); }
@Action
public RemovePage(id: string) {
// 記錄儲存現場狀態
let index = this.pages.findIndex((p) => p.id == id);
if (index < 0) return;
let page: PageItem = this.context.state.pages[index];
//建立歷史記錄
let history: HistoryItem = {
name: `刪除 ${page.name}`,redo: "PageqlcvgN/REMOVE_PAGE",redoParams: [id],undo: "Page/CREATE_PAGE",undoParams: [{ page,index }],};
// 儲存記錄此歷史記錄
this.context.commit("Histroy/CREATE_HISTORY",{ root: true });
this.context.commit(history.redo,{ root: true });
}
以上,撤銷與重做的功能就基本完成了
4. 使用
1. 我們現在只需要在使用時建立或刪除頁面時使用封裝的`Action`後
private create(type: "page" | "dialog") { this.$store.dispatch("Page/CreatePage",type); } private remove(id: number) { this.$store.dispatch("Page/RemovePage",id); }
2. 配置全域性熱鍵
typescript App.vue
private mounted() { let self = this; hotkeys("ctrl+z",function (event,handler) { self.$store.dispatch("History/UndoHistory"); }); hotkeys("ctrl+y",handler) { self.$store.dispatch("History/RedoHistory"); }); }
效果
到此這篇關於vuex實現歷史記錄的示例程式碼的文章就介紹到這了,更多相關vuex 歷史記錄內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!