行业资讯
从WinFroms到Vue:我为什么决定在Web上做一套GUI框架?
我最早习惯的是 WinForms 一类桌面 GUI 开发后来逐渐转向 Vue在 Web 上开发企业应用。刚开始我把这件事理解成一次普通的技术栈迁移从 C# 转到 JavaScript从 WinForms 转到 Vue。真正做下来以后我才意识到这并不只是换了编程语言和框架而是在两套不同的 UI 开发模型之间切换。WinForms 面向的是桌面 GUI。开发者直接使用 Button、TextBox、DataGrid、Window 和 DockPanel 这样的控件通过对象、属性、方法和事件组织应用。Vue 面向的是 Web。开发者使用状态描述页面再由响应式系统、组件和 DOM 完成界面更新。这两种模型没有简单的先进与落后之分。Vue 让 Web 开发变得高效、灵活也让团队更容易招聘、协作和快速交付。对于企业官网、内容平台、电商前台、用户门户和普通后台页面我仍然会优先选择 Vue 和成熟的 Web 组件库。但当我开始开发 ERP、财务系统、临床信息系统、电子病历编辑器、报表设计器以及高密度业务工作台时我越来越明显地感到这些系统虽然运行在浏览器里使用方式却更接近桌面软件。我真正不适应的不是 JavaScript也不是 Vue 的语法。我不适应的是原本应该由 GUI 框架承担的复杂度开始越来越多地落到业务开发人员身上。一、运行在浏览器里不等于它只是一张网页现代 Web 开发经常把不同类型的产品放进同一个分类中。企业官网是 Web。电商商城是 Web。ERP 是 Web。医院临床系统也是 Web。但它们面对的并不是同一种 UI 问题。内容型网站通常以阅读和信息浏览为主• 页面按区块组织• 用户沿着自然文档流浏览• 点击链接进入另一个页面• 填写相对独立的表单• 需要适配手机、平板和桌面端• 关注 SEO、可访问性和加载速度DOM 和 CSS 非常适合这类产品。内容本身就是页面结构浏览器已经提供了成熟的排版、语义和交互能力。而 ERP、财务、临床系统和专业设计工具通常呈现出另一种形态• 用户长时间停留在同一个工作区• 一个屏幕中同时展示大量信息• 工具栏、导航树、属性面板和主工作区需要协同• 大量使用表格、树、表单、窗口和弹层• 强依赖键盘、焦点、选区和连续编辑• 页面之间需要保留未完成的状态• 对空间利用率和操作节奏要求很高例如在一套临床信息系统中医生可能需要同时查看患者基本信息、医嘱、检验结果、检查报告、病历文书、诊断和过敏史并在多个患者和多项业务之间频繁切换。患者列表首先是一个独立工作页医生从病区患者中定位对象再打开并保留对应的就诊工作台进入患者后患者信息、业务页签、筛选、编辑表格和临时选择器会共同存在于同一个持续工作的界面中这些内容不是为了让医生“阅读一张网页”而是为了让他连续完成专业工作。所以Web 的交付方式与网页式的 UI 模型并不是一回事。一个应用可以通过浏览器部署、更新和访问同时仍然需要桌面 GUI 的信息密度、交互连续性和工作区组织能力。二、我怀念的不是 WinForms而是 GUI 的确定性在 WinForms 中一个按钮就是一个明确的控件对象。var button new Button{Text 保存,Width 100,Height 32};button.Click (_, _) Save();toolPanel.Controls.Add(button);我创建了一个对象这个对象就是界面中的控件。我可以保存它的引用读取它的状态调用它的方法监听它的事件也可以把它放进另一个容器。组件、布局、事件、焦点和生命周期属于同一个 GUI 世界。到了 Vue 中同样的按钮可以写得非常简洁Button :disabledsaving :loadingsaving clickhandleSave /这种声明式写法很适合状态驱动的页面。开发者描述当前状态下界面应该是什么样子框架负责完成依赖收集、更新调度和 DOM 修改。在普通页面中这种方式比手动操作界面对象高效得多。但对于复杂企业软件我经常需要的不只是“状态变化以后页面应该长什么样”还需要一个可以被明确调用、具有稳定行为和生命周期的专业控件。以前使用 DevExpress 时我很少需要考虑一张表格内部如何虚拟化、编辑器如何复用、键盘导航怎样建立、焦点怎样移动。开发人员面对的是一张已经完成这些工作的 DataGrid然后把精力放在业务数据、校验规则和操作流程上。我怀念的不是某个陈旧 API也不是把 Web 写回桌面程序。我怀念的是这种确定性开发者操作的是业务控件框架承担控件内部的复杂度。三、响应式适合描述结果但不适合承载所有过程Vue 的响应式系统是它最有价值的能力之一。修改状态界面自动更新。用户资料、商品列表、查询页面和普通表单本来就可以自然地理解为数据状态的视觉结果。但专业软件中也有很多具有明确开始、持续和结束的过程例如拖拽、框选、连续编辑和窗口交互。它们当然都可以在 Vue、React 和 DOM 中实现区别不在于“能不能做”而在于开发者需要通过什么方式表达和追踪这个过程。如果一个原本线性的交互被拆成多份状态、监听和组件间副作用开发者排查问题时就需要重新拼出它的因果关系。对于这类场景允许业务代码直接表达事件流并不比声明式落后反而更贴近过程本身。一个更常见、也更贴近业务开发的例子是只在特定流程中出现的临时窗口。这里说的不是只有一段提示文字的确认框。ElMessageBox.confirm() 之类的通用方法已经可以很好地解决这种需求没有必要为此重新建立一套 GUI 模型。更典型的是临床系统中的“检验申请保存并提交”。医生点击提交后需要在临时窗口中再次核对患者和检验项目补充主诉、临床诊断与标本信息决定是否加急校验通过后才真正提交。这不是一个通用 Confirm而是一个只在特定业务分支中出现的完整表单。在 Vue 中可以把表单草稿和校验全部封装进弹窗组件页面只保留一个当前申请状态。即使按照这种更精简的方式编写弹窗仍然需要成为页面组件结构的一部分pendingInspection 为 null 时弹窗实例并没有挂载。但为了支持这个偶发流程页面仍然需要把它纳入自己的状态模型、事件处理和组件结构。React 中通过 state 和条件 JSX 渲染同类业务窗口也存在相同的结构。对于页面的主要布局这种声明方式很自然但随着低频业务窗口不断增加开发人员阅读一个页面时也需要同时理解这些当前并未发生的分支。问题不是多写了几行代码而是临时流程被提升成了页面长期需要承担的结构和状态。在 ds-ui 中页面可以在提交动作发生时才创建本次草稿和业务窗口并在同一段逻辑中等待结果这里的 InspectionSubmitWindow 是业务项目基于 ds-ui 封装的独立窗口控件而不是框架内置的通用确认框。它内部可以包含患者与检验项目核对、主诉、临床诊断、标本类型、标本说明、加急标记和提交校验但这些细节属于窗口自身不需要散落在打开它的页面中。这里更接近 WinForms 的对象式、命令式控件模型需要时创建交给窗口服务显示关闭后返回结果。取消时本次局部 draft 随过程结束而丢弃确认时业务逻辑使用它继续提交。临时状态不需要被提升为页面级常驻状态窗口也不需要进入页面的主要组件结构。这种写法带来的价值首先是更低的心智负担。开发人员审查代码或排查问题时可以从按钮事件或业务命令出发沿着“创建窗口—等待结果—继续提交”的事件流和业务流很快找到对应逻辑不需要在页面状态、组件声明和多组回调之间来回跳转。业务窗口仍然可以独立组合、校验和测试并不是把所有代码都堆进 click 事件。页面只保留流程窗口负责自己的字段和规则框架负责模态关系、焦点、关闭和释放三者的边界非常明确。Vue 和 React 也可以通过 Modal Service、Portal 或动态挂载形成类似 API。真正的差别是在 ds-ui 中这不是绕开页面模型的补充技巧而是 Window、Dialog 和临时工具共同遵循的一等生命周期模型。在这个具体场景里更先进的不是 Canvas 绘制而是更接近业务意图的抽象Vue 把临时业务窗口作为页面结构的一部分声明ds-ui 把它作为一次按需发生、可以等待结果的业务过程。四、组件越来越多业务不一定越来越清楚Vue 的组件化是正确而重要的能力。合理的组件边界可以隔离状态、复用实现也可以缩小一次更新影响的范围。问题在于组件化有时会退化成把一个页面拆成更多文件。PatientPage├─ PatientToolbar├─ PatientInfo├─ OrderList├─ MedicalRecord├─ InspectionPanel└─ EditDialog文件看起来更加整齐但这些组件可能仍然通过大量 Props、Emit、Ref 和 Store 紧密协作。一次保存操作可能需要从 Dialog 发出事件由 Page 调用接口再修改 Store随后 List、Toolbar 和 Dialog 分别响应变化。系统并不是没有耦合而是把直接调用关系转换成了事件和状态传播关系。复杂页面继续拆分以后组件边界还会同时承担• 更新范围• 状态归属• 生命周期• 数据传递• 事件传递• 代码组织拆得太粗一处状态变化可能影响更大的组件子树拆得太细页面又会产生大量实例、依赖、传递关系和生命周期。为了避免逐层传递可以使用 Store、provide/inject、Composable 或事件总线。这些都有适用场景但工具越来越多并不意味着业务结构一定更容易理解。真正的组件化应该意味着明确的职责、稳定的接口、可预测的行为和独立的生命周期而不是简单地把同一个业务过程分散到更多文件中。我更希望复杂控件在内部横向拆分职责例如将数据模型、视口、绘制、编辑和交互分别放在同一层级的模块中业务代码则继续面对一个完整、稳定的控件而不是为了获得更新精度不断增加业务组件的嵌套深度。五、能实现不等于应该让每个业务项目重新实现上一篇文章发布以后有人提到大表格可以使用虚拟滚动和视图回收没有看到使用 Canvas 的必要性也有人把重新建立布局、事件和焦点系统理解为重复造轮子。这些疑问是合理的但我并不想证明“只有 Canvas 才能做大表格”。DOM 虚拟滚动可以解决许多问题成熟的 VTable 也使用 Canvas 并只绘制可视区域。如果项目只缺一张大表格现有组件已经满足需求直接使用它就是更合理的选择。我更关心的是业务开发人员拿到的究竟是一项还需要继续组装的底层技术还是一个可以直接进入业务流程的完整控件。虚拟化、编辑器复用、键盘导航、焦点恢复和刷新调度都应该由框架内部处理而不是成为每个医嘱、财务或 ERP 页面开发者必须理解的前置知识。因此下面这张图描述的是框架内部如何控制大数据量而不是业务开发人员应当如何写页面。对使用者来说这些复杂度最好根本不需要出现。六、为什么最终不是一张表格而是一套完整 GUI 框架一旦临时窗口被建模成按需发生的业务过程问题就不再只是怎样画一张表格。打开窗口之前可能来自按钮、菜单或快捷键窗口内部需要输入、选择、校验和焦点移动关闭以后还要把结果交还给页面、命令或另一个窗口。Button、Input、DataGrid、Tree、Menu 和 Window 如果各自使用不同的事件、焦点、弹层和生命周期规则业务开发人员最终仍然需要在页面里把它们重新粘合起来。只有这些控件共享同一种对象模型和运行机制“沿着事件流和业务流阅读代码”才可能成为整套应用的一致体验。因此ds-ui 需要统一的组件关系、布局、事件、焦点、输入、弹层、页面生命周期、刷新和诊断。完整 GUI 框架的意义不是组件数量更多而是这些能力不再以互不相关的插件形式暴露给业务项目。对于只有局部复杂区域的项目在 Vue 页面中嵌入专业表格或 Canvas 区域完全合理。但如果产品主体就是长期驻留的工作台、表格、窗口和编辑器同时维护两套组件树、两套焦点规则和两套生命周期未必比使用一套完整 GUI 运行时更简单。它要提供的不只是一些看起来风格统一的组件而是一种统一的开发模型Application↓Window / Page / Workspace↓Component Tree↓Layout / Event / Focus / Input↓Render业务开发者面对的是 Application、Page、DataGrid、Tree、Dialog 和 Editor。至于布局怎样计算、事件怎样路由、可视内容怎样更新应该由框架负责。七、Canvas 是实现手段也决定了它的边界选择 Canvas 并不代表问题自动消失。浏览器原本提供的许多能力需要由框架重新承接自研运行时也不天然更快。Canvas 让我可以围绕专业 GUI 建立统一的组件和运行机制但绘制、缓存、调度和资源释放都应该被框架封装起来。业务开发人员面对的仍然应当是 Button、DataGrid、Tree、Window 和 Editor而不是 Canvas API。浏览器已经成熟的输入法和剪贴板等能力也继续由框架内部复用。技术最终是为开发服务的。如果使用 ds-ui 反而要求业务开发人员先学习绘制、虚拟化和帧调度它就没有完成自己的工作。同样这套模型并不适合所有 Web 应用。如果产品主要是内容展示、信息浏览、普通表单和页面跳转Vue、React、DOM 和 CSS 已经提供了更成熟的方式普通增删改查后台也没有必要使用 ds-ui。Canvas 的可访问性需要额外建设因此它目前也不适合门户、内容网站和面向大众用户的普通前台产品。它更适合高信息密度、长时间运行、强键盘操作、复杂表格和编辑器、多页面状态保留以及多面板协同的专业软件。它的价值和代价来自同一个选择放弃一部分浏览器原生 UI 能力换取一套更可控、更统一的专业 GUI 运行时。八、从 WinForms 到 Vue我真正想重新建立的是什么回头看从 WinForms 转到 Vue我真正不适应的不是语法也不是所谓的前后端思维。我明明在开发一套长期运行、持续交互的 GUI 软件却需要让业务结构不断迁就页面、组件和状态传播关系。在 WinForms 中一个控件就是一个控件一次调用通常意味着一个明确行为焦点、窗口层级和控件树属于同一套系统。在 Vue 中这些能力都可以实现。但当页面越来越复杂开发人员需要理解的组件边界、响应式依赖、状态传播、DOM 行为和第三方组件细节也会越来越多。我想重新建立的不是 WinForms 的旧 API也不是一个披着桌面外观的网页。我想重新建立的是一种完整的 GUI 开发体验组件、布局、事件、焦点、输入和生命周期属于同一个世界框架承担底层复杂度业务开发人员专注于业务。在高信息密度、长时间运行、强交互的专业软件中我认为完整 GUI 运行时是一种比普通 Vue 页面模型更先进的抽象。先进的不是 Canvas 绘图 API而是业务控件具有稳定对象身份临时界面可以按需发生事件流可以直接追踪焦点、窗口和生命周期也由同一个运行时管理。Vue 仍然是大量 Web 产品更合适的选择。ds-ui 所解决的是另一类问题让那些通过浏览器交付、使用方式却已经更接近专业桌面软件的应用拥有一套统一、直接、可预测的开发基础。
郑州网站建设
网页设计
企业官网