最近做的项目中, 遇到了需要记录某个数据对象的历史变更记录, 需要实现数据变更的撤销和重做
比如数据 a 初始为 a = {}
历史记录1 a = { b: 0 }
历史记录2 a = { b: 0, c: [] }
历史记录3 a = { b: 1, c: [ 1, 2 ] }
最简单的思路就是 用一个数据记录所有的历史记录 根据一个历史记录索引 取出历史记录 重新赋值给a
这种方式在数据量比较小的时候, 可以考虑使用, 但是一旦数据量比较大, 很容易就把浏览器内存撑爆
另外数据的变动细节没有体现, 在数据的变动细节基础上做一些额外的功能就无法实现了
查找资料的过程中, 看到了deep-diff, 可以获取两个对象差异, 并且将差异应用到对象上
https://www.npmjs.com/package/deep-diff
于是在此基础上, 形成了如下方案
const { diff, applyChange, revertChange } = require('deepdiff')
/**
* 基于deepdiff 大数据量时的 数据的历史记录操作
*/
class History {
constructor (max = 100) {
// 当前位置索引 根据此索引做前进后退
this.index = 0
// 历史记录
this.list = []
// 最后记录的完整数据
this.last = null
// 历史记录最大数量
this.max = max
// 需要记录变更历史的数据
this.current = null
}
/**
* 初始化 重置记录索引 清空历史记录 关联数据
* @param base
*/
init (base) {
this.index = 0
this.list = []
this.last = JSON.parse(JSON.stringify(base))
this.current = base
}
/**
* 添加历史记录
*/
add () {
// 获取差异 添加记录
const d = diff(this.last, this.current)
if (!d) {
return
}
// 超过最大数量 删除开头的记录
if (this.list.length === this.max) {
this.list.splice(0, 1)
this.index -= 1
}
// 移除当前记录点后面的记录
this.list.splice(this.index)
this.list.push(d)
this.index += 1
this.last = JSON.parse(JSON.stringify(this.current))
}
/**
* 撤销
*/
back () {
return new Promise(resolve => {
if (this.index > 0) {
this.index -= 1
const d = this.list[this.index]
for (const obj of d) {
revertChange(this.current, this.last, obj)
}
this.last = JSON.parse(JSON.stringify(this.current))
resolve(d)
}
})
}
/**
* 重做
*/
redo () {
return new Promise(resolve => {
if (this.index < this.list.length) {
const d = this.list[this.index]
for (const obj of d) {
applyChange(this.current, this.last, obj)
}
this.last = JSON.parse(JSON.stringify(this.current))
this.index += 1
resolve(d)
}
})
}
}
module.exports = History
可以设定记录总数 超出数量 删除最早的记录
使用方法:
const History = require('./History')
const history = new History(5)
const data = {}
history.init(data)
data.a = {}
history.add()
data.b = 5
history.add()
history.back()
history.back().then(change => {
// ...
})
history.redo()
history.redo().then(change => {
// ...
})
deep-diff 默认不支持vue2 所以以上方法在vue2中会有问题 数据改变了 但是不会触发更新
针对vue2问题 写了个npm库 在deep-diff基础上添加了vue2的支持
npm: npm install --save @xpf0000/js-data-history
git: https://github.com/xpf0000/js-data-history
欢迎大家交流讨论