文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么用Immutable.js实现撤销重做功能

2024-04-02 19:55

关注

这篇文章主要介绍了怎么用Immutable.js实现撤销重做功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Immutable.js实现撤销重做功能文章都会有所收获,下面我们一起来看看吧。

第一步:确定哪些状态需要历史记录,创建自定义的 State 类

并非所有的状态都需要历史记录。许多状态是非常琐碎的,尤其是一些与鼠标或者键盘交互相关的状态,例如在画图工具中拖拽一个图形时我们需要设置一个「正在进行拖拽」的标记,页面会根据该标记显示对应的拖拽提示,显然该拖拽标记不应该出现在历史记录中;而另一些状态无法被撤销或是不需要被撤销,例如网页窗口大小,向后台发送过的请求列表等。

排除那些不需要历史记录的状态,我们将剩下的状态用 Immutable Record 封装起来,并定义 State 类:

// State.ts

import { Record, List, Set } from 'immutable'

const StateRecord = Record({

  items: List<Item>

  transform: d3.ZoomTransform

  selection: number

})

// 用类封装,便于书写 TypeScript,注意这里最好使用Immutable 4.0 以上的版本

export default class State extends StateRecord {}

这里我们的例子是一个简易的在线画图工具,所以上面的 State 类中包含了三个字段,items 用来记录已经绘制的图形,transform 用来记录画板的平移和缩放状态,selection 则表示目前选中的图形的 ID。而画图工具中的其他状态,例如图形绘制预览,自动对齐配置,操作提示文本等,则没有放在 State 类中。

第二步:定义 Action 基类,并为每种不同的操作创建对应的 Action 子类

与 redux-undo 不同的是,我们仍然采用命令模式:定义基类 Action,所有对 State 的操作都被封装为一个 Action 的实例;定义若干 Action 的子类,对应于不同类型的操作。

在 TypeScript 中,Action 基类用 Abstract Class 来定义比较方便。

// actions/index.ts

export default abstract class Action {

  abstract next(state: State): State

  abstract prev(state: State): State

  prepare(appHistory: AppHistory): AppHistory { return appHistory }

  getMessage() { return this.constructor.name }

}

Action 对象的 next 方法用来计算「下一个状态」,prev 方法用来计算「上一个状态」。getMessage 方法用来获取 Action 对象的简短描述。通过 getMessage 方法,我们可以将用户的操作记录显示在页面上,让用户更方便地了解最近发生了什么。prepare 方法用来在 Action 第一次被应用之前,使其「准备好」,AppHistory 的定义在本文后面会给出。

Action 子类举例

下面的 AddItemAction 是一个典型的 Action 子类,用于表达「添加一个新的图形」。

// actions/AddItemAction.ts

export default class AddItemAction extends Action {

  newItem: Item

  prevSelection: number

  constructor(newItem: Item) {

    super()

    this.newItem = newItem

  }

  prepare(history: AppHistory) {

    // 创建新的图形后会自动选中该图形,为了使得撤销该操作时 state.selection 变为原来的值

    // prepare 方法中读取了「添加图形之前 selection 的值」并保存到 this.prevSelection

    this.prevSelection = history.state.selection

    return history

  }

  next(state: State) {

    return state

      .setIn(['items', this.newItem.id], this.newItem)

      .set('selection', this.newItemId)

  }

  prev(state: State) {

    return state

      .deleteIn(['items', this.newItem.id])

      .set('selection', this.prevSelection)

  }

  getMessage() { return &mdash;&mdash;Add item ${this.newItem.id}&mdash;&mdash; }

}

运行时行为

应用运行时,用户交互产生一个 Action 流,每次产生 Action 对象时,我们调用该对象的 next 方法来计算后一个状态,然后将该 action 保存到一个列表中以备后用;用户进行撤销操作时,我们从 action 列表中取出最近一个 Action 并调用其 prev 方法。应用运行时,next/prev 方法被调用的情况大致如下:

// initState 是一开始就给定的应用初始状态

// 某一时刻,用户交互产生了 action1 ...

state1 = action1.next(initState)

// 又一个时刻,用户交互产生了 action2 ...

state2 = action2.next(state1)

// 同样的,action3也出现了 ...

state3 = action3.next(state2)

// 用户进行撤销,此时我们需要调用最近一个action的prev方法

state4 = action3.prev(state3)

// 如果再次进行撤销,我们从action列表中取出对应的action,调用其prev方法

state5 = action2.prev(state4)

// 重做的时候,取出最近一个被撤销的action,调用其next方法

state6 = action2.next(state5)

Applied-Action

为了方便后面的说明,我们对 Applied-Action 进行一个简单的定义:Applied-Action 是指那些操作结果已经反映在当前应用状态中的 action;当 action 的 next 方法执行时,该 action 变为 applied;当 prev 方法被执行时,该 action 变为 unapplied。

关于“怎么用Immutable.js实现撤销重做功能”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“怎么用Immutable.js实现撤销重做功能”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯