ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

02-设计哲学:从DataWindow到Vue3

02-设计哲学:从DataWindow到Vue3 02-设计哲学从PowerBuilder DataWindow到Vue3browise 最核心的设计不是某个技术选型是一条贯穿14年的思想主线——PowerBuilder 的 DataWindow 数据窗口。这篇文章讲清楚一个2000年代的C/S控件思想怎么在 Vue3 Spring Boot 上复活为什么它的核心抽象脏标记行集值得跨语言传承。文章目录02-设计哲学从PowerBuilder DataWindow到Vue3一、DataWindow 是什么二、这个思想传到 browise 经过了两次换语言三、browise 前后端镜像的三个对象3.1 Row一行数据 脏标记3.2 RowSet三缓冲区3.3 collect(dirty)Unit of Work四、为什么不用 Pinia/Vuex 管理这个状态五、这个设计的当代回响一、DataWindow 是什么PowerBuilder 的 DataWindow 不是一个表格控件。它是一个自带数据生命周期管理的行集对象DataWindow ├── 数据源SQL/存储过程 ├── 三缓冲区Primary!/Filter!/Delete! ├── 行状态New!/NewModified!/DataModified!/NotModified! ├── Update() —— 一次性提交所有变更 ├── AcceptText() / Reset() —— 确认与回滚 └── 事务联动success → AcceptTextfail → 不动用 PB 开发过的人对这个流程很熟dw_1.Retrieve() 取数 → 用户在界面上改 → dw_1.Update() 时框架自动把 NewModified 行拼 INSERT、DataModified 行拼 UPDATE、Deleted 行拼 DELETE——一个方法提交三种变更。这比表单提交的模型高一个抽象级表单模型一次提交一行DataWindow 模型一次提交一个变更集。二、这个思想传到 browise 经过了两次换语言PowerBuilder DataWindow (1990s, C/S) │ ▼ 第一次Java JavaScript wiserise 时代 后端 com.bluepointsoft.core.util.ds.Row / RowSet / DataStore / DataCenter 前端 DataWindow.js3226行手写JS复刻PB行集 通信协议 Row { _t: 0|1|3|4, _o: {旧值}, ...字段 } │ ▼ 第二次TypeScript Vue3 browise 时代 前端 src/core/useRowSet.ts220行Vue3组合式API复刻 后端保留 Row.java/BaseEntity.java 承接同一协议两次迁移协议没变——_trow type0未改/1新增/3修改/4删除和_ooriginal修改前的旧值这两个字段名14年没动过。为什么协议能稳定因为它是最小完备的行级状态一个字段_t够了字段级回滚一个对象_o够了。加任何东西比如修改时间、修改人都是业务字段不属于协议。三、browise 前后端镜像的三个对象3.1 Row一行数据 脏标记前端browise-vue/src/core/useRowSet.ts// 行状态枚举types.tsexportenumRowStatus{NONE0,INSERT1,UPDATE3,DELETE4}// Row 的核心行为setItemValue(key,value){if(this._tRowStatus.NONE){this._tRowStatus.UPDATE// 首次修改NONE → UPDATEthis._o[key]旧值// 同时记录原始值}// 注意已经是 UPDATE 的行再改_o 不覆盖// ——保留的是最初值而不是上一次值}后端browise-data/.../Row.java5.3KB是同一套语义的Java版。两边各写了一遍但行为契约完全一致——这个契约就是靠 _t/_o 两个协议字段锁定的。3.2 RowSet三缓冲区primary[] 当前有效行含 NONE/INSERT/UPDATE 状态 filter[] 筛选出来的行 delete[] 被删除的行DELETE状态可恢复关键操作的三缓冲行为操作INSERT行UPDATE行NONE行remove()直接从primary移除新加的没提交删了就没了移入delete[]并标DELETE移入delete[]并标DELETEcollect(‘dirty’)收集收集跳过rejectChanges()直接删除撤销新增从_o逐字段恢复状态回NONE不动acceptChanges()状态归NONE清_o清delete[]同左不动remove 的分支逻辑最能体现设计新增未提交的行删除时不需要进 delete 缓冲——数据库里根本没有它回滚也不需要恢复它。3.3 collect(‘dirty’)Unit of Workcollect()收集全部脏行按 _t 分组成提交报文{storeName:{ins:SYS_USER_i,up:SYS_USER_u,del:SYS_USER_d,rows:[{_t:1,name:张三},{_t:3,name:李四,_o:{name:李三}},{_t:4,id:005}]}}后端EaController.commonSave()收到后遍历 rows按 _t 分发到三个 sqlId 执行。前端收集一次后端一个事务内完成三种DML——这就是 PB 的 dw.Update() 在前后端分离时代的形态。四、为什么不用 Pinia/Vuex 管理这个状态browise-vue 的状态管理是useCenter.ts——基于 Vue3 provide/inject 的 Store 注册中心没有引入 Pinia。理由写在设计里RowSet 的状态是数据状态不是应用状态。应用状态当前用户、主题、语言适合集中式store数据状态某行某字段改没改过天然属于数据本身——挂在 Row 上比挂在外部store更符合直觉。useCenter 只做三件事addStore(name) 注册一个 StoreStaterowset page meta loading各组件通过 useBinding(field) 字段级绑定到 store.rowset 当前行toPayload() 一次收集所有 store 的脏数据没有 getter/mutation/action 那套——因为变更追踪已经在 Row._t/_o 里了不需要框架再追踪一遍。五、这个设计的当代回响值得注意这个思想正在被主流生态重新发现。TanStack Table 的行编辑模型、AG Grid 的 transaction API都在做行级状态批量提交前端几年流行的乐观更新回滚本质是 rejectChanges 的手工版PostgreSQL 的 EXCLUDE 约束、JPA 的 dirty checking是同一思想在数据库/ORM侧的投影PB 死了DataWindow 的抽象没死——它只是换了个名字活在每个需要编辑数据集的场景里。✅ 亮点以 DataWindow 思想的14年跨语言传承为主线讲清 _t/_o 协议为什么14年稳定最小完备、三缓冲的remove分支为什么那样设计、为什么不用Pinia数据状态vs应用状态。适合做前端数据层设计的参考。扩展方向第17-19篇逐行拆useRowSet.ts、第44篇讲它和Row.java的镜像实现。
返回列表