vuex实现历史记录
2021年01月25日 技术分享>前端技术 JavaScript, Vue, Vuex, 思路本站文章除注明转载外,均为原创文章。如需转载请注明出处:
https://kwokronny.top/202101/vuex-develop-history/

最近自研着一个可视化操作平台,其中涉及到用户操作后可撤销或重做,在网上搜了一些解决思路,完善自己所设想的解决思路。
历史记录需求的要点
- 可存储在 localStorage 中
- 可多次撤销或多次重做
- 点击列表中的一项,将历史倒退或前进至指定位置
看似简单的需求,在基础建设设计上的错误,亦会在未来导致更多的工作量。所以结合上面两点的要求,发现 vuex 的基本思路非常适合完成这个需求,redux 同样。
实现思路
此项目用了 typescript 来加强代码的严谨性,方便日后维护,大家简单看个思路。
1. 先定义历史记录的数据结构
1 |
|
2. 编写 History 状态模块
编写基础操作history状态的vuex module
,创建记录的Mutation
,重做和撤销的Action
一条记录是包含对这个步骤的执行
redo
操作与撤销undo
操作的。所以在用户点击列表其中一项时,应该是循环回退到当前项的前一项undo,或循环redo到当前项
所以需要增加一条空记录,方便用户点击空记录撤销最初的操作。运用了
vuex-module-decorators
装饰器,写更易维护的代码
1 |
|
3. 编写可以撤销或重做的功能
完成上面两步后,我们就可以编写各种操作了
编写对数据基础操作的
Mutation
1
2
3
4
5
6
7
8
9
10
11
12
13
14@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;
}
}将基础操作按要求封装成带保存->记录->执行的
Action
store/modules/Page.ts 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 包装创建页面函数
@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}`,
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 });
}
1 |
|
以上,撤销与重做的功能就基本完成了
4. 使用
1. 我们现在只需要在使用时创建或删除页面时使用封装的`Action`后
1
2
3
4
5
6
7
private create(type: "page" | "dialog") {
this.$store.dispatch("Page/CreatePage", type);
}
private remove(id: number) {
this.$store.dispatch("Page/RemovePage", id);
}
2. 配置全局热键
App.vue 1
2
3
4
5
6
7
8
9
10
11
...
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");
});
}
...
效果

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!


