44-useRowSet镜像实现

44-useRowSet:220行镜像后端的行集实现

前端useRowSet.ts与后端Row.java是同一套语义的两种语言实现------_t/_o/三缓冲/collect/accept/reject一字不差。但这220行不是翻译------Vue3的reactive让"改字段界面自动更新"免费获得。这篇逐段对照Java版讲TypeScript实现,重点在差异处。

文章目录

源码:browise-vue/src/core/useRowSet.ts(220行)

测试:useRowSet.test.ts(25个用例)


一、Row类:与Java版的逐行对照

typescript 复制代码
class Row {
  _data: RowData                    // Java版: LinkedHashMap data
  _t: RowStatus = RowStatus.NONE    // 同名
  _o: RowData = {}                  // 同名

  constructor(data?: RowData) {
    this._data = reactive(data ? { ...data } : {})   // ★关键差异
  }

  setItemValue(key: string, value: any): void {
    if (this._t === RowStatus.NONE) {
      if (this._o[key] === undefined && this._data[key] !== undefined) {
        this._o[key] = this._data[key]       // 首改记旧值
      }
      this._t = RowStatus.UPDATE
    }
    this._data[key] = value
  }
}

与Row.java的差异三处

Java版(第17篇) TS版
数据载体 LinkedHashMap reactive({})------Vue响应式代理
_o记录条件 containsKey即记 undefined判断(TS没有null Map语义)
额外能力 getMap返回引用 clone()、toRaw()浅拷贝

reactive是最大差异也是最大免费午餐 ------this._data[key] = value这行在Java里只是改Map,在Vue3里自动触发依赖此字段的组件重渲染 。不需要手写"字段变了通知界面"------表单控件绑了_data.name,改值输入框自动更新。脏标记与响应式两层追踪并存:响应式管"界面刷新",_t/_o管"提交什么"。


二、remove的INSERT分岔:语义完全一致

typescript 复制代码
function remove(row: Row): void {
  const idx = buffer.primary.indexOf(row)
  if (idx >= 0) {
    buffer.primary.splice(idx, 1)
    if (row._t === RowStatus.INSERT) {
      return                      // INSERT直接删完事
    }
    row._t = RowStatus.DELETE
    buffer.delete_.push(row)       // 非INSERT进delete缓冲
  }
}

与Java版第18篇一模一样的分岔 ------INSERT行删除不进delete_缓冲。delete_的尾下划线是TS命名(delete是保留字)。


三、collect的三模式:比Java版多两个

typescript 复制代码
function collect(mode: 'auto' | 'all' | 'dirty' = 'auto'): RowData[] {
  if (mode === 'all')    return primary.map(r => r.toRaw())          // 纯数据
  if (mode === 'dirty') {
    // primary的脏行 + delete_的全部行,带_t/_o
    const raw = { ...r.toRaw(), _t: r._t }
    if (Object.keys(r._o).length > 0) raw._o = { ...r._o }
    result.push(raw)
  }
  // auto(默认)= primary全部带_t(不含_o)
  return primary.map(r => ({ ...r.toRaw(), _t: r._t }))
}
模式 内容 场景
auto 全部行+_t 常规提交(后端按_t分发)
all 纯数据(无_t/_o) 导出/打印快照
dirty 脏行+_t/_o commonSave提交报文(第23篇)

Java版只有一种collect(=dirty)------TS版多了auto/all是前端场景更杂(导出要all、调试要auto)。

_o的条件附加 ------if (Object.keys(r._o).length > 0)------空_o不输出,报文瘦身(新增行根本没有_o,别传个空对象占字节)。


四、rejectChanges:TS版比Java版多做了一件事

typescript 复制代码
function rejectChanges(): void {
  for (let i = primary.length - 1; i >= 0; i--) {   // 倒序删------与Java同
    const r = primary[i]
    if (r._t === RowStatus.INSERT) {
      primary.splice(i, 1)
    } else if (r._t === RowStatus.UPDATE) {
      for (const key of Object.keys(r._o)) {
        r._data[key] = r._o[key]       // ★逐字段恢复------不是clear+putAll
      }
      r._t = RowStatus.NONE
      r._o = {}
    }
  }
  // ★delete_缓冲的行回归primary------Java版没有!
  for (const r of buffer.delete_) {
    r._t = RowStatus.NONE
    r._o = {}
    buffer.primary.push(r)
  }
  buffer.delete_ = []
}

两处与Java版的差异

①逐字段恢复 vs 整体替换 ------Java版(修复后)用clear+putAll(originals)整体替换;TS版逐字段回填_o的key。语义等价(_o只含改过的字段,没改的字段_data值本来就是原值)------TS版的逐字段更符合直觉,且不会误删_o之外的字段。

②delete_回归primary ------Java版直接clear丢弃(第19篇讲过这是已知边界) ,TS版把删除的行恢复回primary前端实现反而更完整------撤销删除在界面上是合理预期(用户删错了点取消,行该回来)。这是前后端行为差异的已知点------后端reject场景少(服务端批处理失败直接回滚事务),前端reject是用户高频操作。


五、25个单测锁定的行为契约

useRowSet.test.ts(25用例)------前后端行为等价的保障

复制代码
- add标记INSERT / remove的INSERT直删 / remove的DELETE入缓冲
- setItemValue首改记_o、再改不覆盖
- collect三模式的内容正确性
- accept清_o / reject恢复 / reject删除行回归
- clone的_t/_o深拷贝

这些用例的断言与Java版BaseEntityTest/RowSetTest的断言语义一致 ------测试是协议的真正文档。前后端各自改实现可以,测试断言改了就说明协议变了------必须两头同步。


六、setData与clear:查询结果的灌入

typescript 复制代码
function setData(rows: RowData[]): void {
  clear()
  for (const data of rows) {
    buffer.primary.push(new Row(data))
  }
}

对应Java版reset------HTTP查询回来灌入 (httpClient的pageQuery→response.data.rows→setData)。clear用splice(0)而非length=0------保持reactive数组的引用(Vue3的响应式对替换引用会丢失依赖)。


✅ 亮点:TS与Java版的三处差异(reactive/_o条件/clone)、collect三模式比Java多的场景、reject的delete_回归是前端更完整的实现、25个单测作为协议真正文档、splice(0)保持响应式引用的细节。与第17-19篇形成前后端镜像闭环。适合实现前端数据层的人。扩展方向:第45篇useBinding、第46篇useCenter、第02篇设计哲学。

相关推荐
bamboolm2 小时前
springboot+Ollama整合
java·ollama
海宇AI2 小时前
微服务架构实战:基于海宇对外投资历史查询服务构建自动化合规审计网关
人工智能·微服务·架构·自动化
我不会起名字3222 小时前
一天一道算法题(34):回溯法的经典例题(子集)
java·数据结构·python·算法·golang·深度优先·力扣
Wang's Blog3 小时前
Java框架快速入门: Spring Security+OAuth2之JWT核心概念与实战
java·spring·log4j
Dreams_l3 小时前
死信队列和延迟队列介绍
java·开发语言
Bs_MoneyMagnet3 小时前
基于springboot+vue的生态果园采摘预约系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·毕业设计·计算机毕业设计
麻瓜code4 小时前
【JUC】AQS enq() 自旋入队
java
这个DBA有点耶4 小时前
银行核心系统数据库迁移怎么选?6 步法+5 个避坑指南
数据库·安全·架构
Wang's Blog4 小时前
Java框架快速入门: Spring Security+OAuth2之核心角色与授权流程
java·spring·github