vuex實(shí)現(xiàn)歷史記錄的示例代碼
最近自研著一個(gè)可視化操作平臺(tái),其中涉及到用戶操作后可撤銷或重做,在網(wǎng)上搜了一些解決思路,完善自己所設(shè)想的解決思路。
歷史記錄需求的要點(diǎn)
- 可存儲(chǔ)在 localStorage 中
- 可多次撤銷或多次重做
- 點(diǎn)擊列表中的一項(xiàng),將歷史倒退或前進(jìn)至指定位置
看似簡(jiǎn)單的需求,在基礎(chǔ)建設(shè)設(shè)計(jì)上的錯(cuò)誤,亦會(huì)在未來導(dǎo)致更多的工作量。所以結(jié)合上面兩點(diǎn)的要求,發(fā)現(xiàn) vuex 的基本思路非常適合完成這個(gè)需求,redux 同樣。
實(shí)現(xiàn)思路
此項(xiàng)目用了 typescript 來加強(qiáng)代碼的嚴(yán)謹(jǐn)性,方便日后維護(hù),大家簡(jiǎn)單看個(gè)思路。
1. 先定義歷史記錄的數(shù)據(jù)結(jié)構(gòu)
interface HistoryItem { timestrap: number; // 記錄時(shí)間戳 name: string; // 記錄名稱 redo: string; // 重做Mutation undo: string; // 撤銷Mutation redoParams: any[]; // 重做Mutation提交參數(shù) undoParams: any[]; // 撤銷Mutation提交參數(shù) } interface HistoryStatus { historys: HistoryItem[]; // 記錄history數(shù)組 _currentHistory: number; // 當(dāng)前節(jié)點(diǎn)索引 }
2. 編寫 History 狀態(tài)模塊
編寫基礎(chǔ)操作history狀態(tài)的vuex module,創(chuàng)建記錄的Mutation,重做和撤銷的Action
一條記錄是包含對(duì)這個(gè)步驟的執(zhí)行redo操作與撤銷undo操作的。所以在用戶點(diǎn)擊列表其中一項(xiàng)時(shí),應(yīng)該是循環(huán)回退到當(dāng)前項(xiàng)的前一項(xiàng)undo,或循環(huán)redo到當(dāng)前項(xiàng)
所以需要增加一條空記錄,方便用戶點(diǎn)擊空記錄撤銷最初的操作。
運(yùn)用了vuex-module-decorators 裝飾器,寫更易維護(hù)的代碼
import { VuexModule, Module, Mutation, Action } from "vuex-module-decorators"; @Module({ namespaced: true }) export class HistoryModule extends VuexModule<HistoryStatus> implements HistoryStatus { /** * 初始化一個(gè)空記錄的原因主要是方便列表操作時(shí): * 當(dāng)用戶點(diǎn)擊最早的一條記錄時(shí),可以正常撤銷用戶操作的第一步 **/ 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 || []; } // 創(chuàng)建歷史記錄 @Mutation public CREATE_HISTORY(payload: HistoryItem) { if (this._currentHistory < this.historys.length - 1) { this.historys = this.historys.slice(0, this._currentHistory); } // 由于js的深淺拷貝問題,所以在創(chuàng)建時(shí)都需要對(duì)數(shù)據(jù)進(jìn)行深拷貝 // 想嘗試lodash的clone函數(shù),但發(fā)現(xiàn)好像JSON.stringify的方式clone應(yīng)該更快的,畢竟我們的數(shù)據(jù)不存在函數(shù) // 我這里就先不改了,主要是表達(dá)出思路即可 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) { let history = historys[current]; if (history) { commit(history.undo, ...history.undoParams, { root: true }); } times--; current--; } commit("SET_CURRENT_HISTORY", current); } }
3. 編寫可以撤銷或重做的功能
完成上面兩步后,我們就可以編寫各種操作了
編寫對(duì)數(shù)據(jù)基礎(chǔ)操作的Mutation
@Mutation public CREATE_PAGE(payload: { page: PageItem; index: number }) { this.pages.splice(payload.index, 0, _.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); if (this._currentPage == index) { this._currentPage = this.pages.length > 0 ? 0 : -1; } }
將基礎(chǔ)操作按要求封裝成帶保存->記錄->執(zhí)行的Action
// 包裝創(chuàng)建頁面函數(shù) @Action public CreatePage(type: "page" | "dialog") { let { state, commit } = this.context; // 記錄保存即將創(chuàng)建的頁面 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 }, }; //創(chuàng)建歷史記錄 let history: HistoryItem = { name: `創(chuàng)建${pageName}`, timestrap: Date.now(), 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, ...history.redoParams, { root: true }); }
@Action public RemovePage(id: string) { // 記錄保存現(xiàn)場(chǎng)狀態(tài) let index = this.pages.findIndex((p) => p.id == id); if (index < 0) return; let page: PageItem = this.context.state.pages[index]; //創(chuàng)建歷史記錄 let history: HistoryItem = { name: `刪除 ${page.name}`, timestrap: Date.now(), redo: "Page/REMOVE_PAGE", redoParams: [id], undo: "Page/CREATE_PAGE", undoParams: [{ page, index }], }; // 保存記錄此歷史記錄 this.context.commit("Histroy/CREATE_HISTORY", history, { root: true }); this.context.commit(history.redo, ...history.redoParams, { root: true }); }
以上,撤銷與重做的功能就基本完成了
4. 使用
1. 我們現(xiàn)在只需要在使用時(shí)創(chuàng)建或刪除頁面時(shí)使用封裝的`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", function (event, handler) { self.$store.dispatch("History/RedoHistory"); }); }
效果
到此這篇關(guān)于vuex實(shí)現(xiàn)歷史記錄的示例代碼的文章就介紹到這了,更多相關(guān)vuex 歷史記錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue.config.js配置報(bào)錯(cuò)ValidationError:?Invalid?options?object解
這篇文章主要給大家介紹了關(guān)于Vue.config.js配置報(bào)錯(cuò)ValidationError:?Invalid?options?object的解決辦法,主要由于vue.config.js配置文件錯(cuò)誤導(dǎo)致的,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02Vue3滑動(dòng)到最右驗(yàn)證功能實(shí)現(xiàn)
在登錄頁面需要啟動(dòng)向右滑塊驗(yàn)證功能,遇到這樣的需求怎么實(shí)現(xiàn)呢,下面小編通過示例代碼給大家分享Vue3滑動(dòng)到最右驗(yàn)證功能實(shí)現(xiàn),感興趣的朋友一起看看吧2024-06-06使用Vue3和Echarts?5繪制帶有立體感流線中國(guó)地圖(推薦收藏!)
最近接到一個(gè)需求是做一個(gè)中國(guó)地圖,下面這篇文章主要給大家介紹了關(guān)于如何使用Vue3和Echarts?5繪制帶有立體感流線中國(guó)地圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04Vue使用Prism實(shí)現(xiàn)頁面代碼高亮展示示例
這篇文章主要為大家介紹了Vue使用Prism實(shí)現(xiàn)頁面代碼高亮展示示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07vue項(xiàng)目如何解決數(shù)字計(jì)算精度問題
這篇文章主要介紹了vue項(xiàng)目如何解決數(shù)字計(jì)算精度問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10vue使用keep-alive保持滾動(dòng)條位置的實(shí)現(xiàn)方法
這篇文章主要介紹了vue使用keep-alive保持滾動(dòng)條位置的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04vue3+ts+vant移動(dòng)端H5項(xiàng)目搭建的實(shí)現(xiàn)步驟
本文主要介紹了vue3+ts+vant移動(dòng)端H5項(xiàng)目搭建,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06antd日期選擇器禁止選擇當(dāng)天之前的時(shí)間操作
這篇文章主要介紹了antd日期選擇器禁止選擇當(dāng)天之前的時(shí)間操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10