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篇设计哲学。

相关推荐
Moshow郑锴1 小时前
sql-pipeline - 基于PostgreSQL的SQL健康检查与发布编排平台
java·sql
Wang's Blog8 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
\光辉岁月/10 小时前
5.java-数组
java·开发语言
谢亮_vipxieliang10 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
郑州光合科技余经理10 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
2601_9620715710 小时前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm
code2cat11 小时前
Java进阶篇之AtomicReference:一次替换引用,读到一份完整状态
java·并发编程·不可变对象·atomicreference
专业程序开发源11 小时前
springboot简历管理系统81389-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
Wang's Blog11 小时前
Java 项目实战: 外卖平台优化-前后端分离开发模式与工程拆分
java·开发语言