可视化业务开发框架解析

为什么做这个框架

  • 降低可视化开发的门槛,解决新人上手成本太高的问题
  • 提升开发效率
  • 提升质量下限

可视化需求和常规前端需求的区别是什么?

可视化组件,会涉及事件、布局、交互、状态等众多内容,不像UI组件,更像一个整合多个UI组件的业务功能模块。
可视化组件中的View,才更像是常规的UI组件库。

代码

business-visualization-template

设计理念

人类设计,代理编码(Human design, Agents code!)

7层人机交互AI Workflow + 软件工程
聚焦问题领域解决方案领域
这个流程和每个环节要做的事情,必须深入思维的习惯里面去,成为原则性的内容。

自顶向下,逐步求精,逐渐固化

越到后面的环节越具体,直至产生原子函数、方法等;
抽象程度也是从大到小变化的。

  • 确定哪些是固定不变的、哪些是会随着需求变动的
  • 不变的部分就形成固化的流程、文档和代码(微内核架构的微内核部分,这部分是我们需要专注做的基建部分,就是基础框架和核心机制)
  • 让AI和人聚焦于变化的部分,缩小上下文和心智负担(即需要通过具体的插件实现的业务功能部分,比如柱状图)

AI + Template(填空题模式)

尽量让AI去填空

流程

  • 需求分析
  • 项目分析
  • 方案设计
  • 模块设计
  • 代码实现
  • 性能优化
  • 可靠性保障

Q&A

这个程序设计怎么来的?

结合业务实践经验 + 参考其他开源可视化项目的设计。

数据相关的处理代码怎么拆分

Data:原始数据的管理,数据预处理、视图元素和原始数据做一个链接(比如getGraphicElement这个方法),这个在3D里面经常用到
Model:数据驱动的核心
Controller里面也有一部分内容

Data的定位

业务无关的数据处理??
需要挂载在Model上
中间层代理???

为什么视图元素和原始数据要做一个链接?

项目中一般存在四类数据:
option:配置项
original data:业务的原始数据
layout data:布局相关的数据
view data:渲染相关的数据

一般是把2、3、4提出来做个链接,其目的是为了解决查询管理问题,提高可维护性,可以理解为是一个微型数据库,算是个cache。

因为我们经常会遇到这个场景:
改了原始数据,需要找到对应的图形元素,更新视觉效果。这就设计model-view的查询。
反过来也可以查询,即通过view获取model,不过这种场景只能通过get方法获取数据,且只能获取数据的副本,不能修改它,否则就违背了单向数据流的原则,容易产生意外的影响。

view data和layout data为什么不能放一起?

O-M-L-V
计算公式+原始数据,获得layout
layout作用于model

layout放view的问题:视图层直接修改了model对象,强耦合了???不是这个原因吧??

Layout怎么处理

Layout算是视图数据下属的一个子分类
也是放Model下的Data里面管理

如何触发事件,进而更新数据?

遵循:注册-绑定-触发 的流程,通过Controller进行调度。

可以看下bar的hover事件的实现示例:

  • Controller中注册事件

    1
    2
    3
    4
    5
    6
    // 悬停状态变更事件
    this.on('barHover', (data: BarChartData | null, index: number | null) => {
    console.trace('controller触发了barHover');

    this.updateBarStyles();
    });
  • View中在bindInteractionEvents()方法中给元素绑定事件

    1
    2
    3
    4
    5
    6
    7
    this.barGroup.on('mouseover', (e: ElementEvent) => {
    const targetRect = e.target as Rect;
    const index = this.bars.indexOf(targetRect);
    if (index !== -1) {
    this.handleInteraction('barHover', { index, event: e });
    }
    });
  • 统一的机制代理触发Controller事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * 处理交互事件 - 子类可选实现
    * @param eventType 事件类型
    * @param eventData 事件数据
    */
    protected handleInteraction(eventType: string, eventData: any): void {
    this.emit('interaction', {
    type: eventType,
    data: eventData,
    target: eventData.target || null
    });

    // 触发具体的事件
    this.controller.emit(eventType, eventData);
    }

Controller中处理了很多业务,是否合理?

不合理,目前的代码中,Controller既处理业务事件又处理UI事件。
需要重构下:

  1. 数据操作移至 model:将数据相关方法移到 BarChartModel
  2. 样式处理移至 view:将 updateBarStyles() 移到 BarChartView
  3. 简化 controller:只保留事件协调和渲染调度逻辑
  4. 添加服务层:复杂的样式计算可抽离为专门的 style service

Element和View的区别是什么?

Element负责图形元素的状态管理

当前的问题

循环依赖

BarChartView 在构造函数中持有了 Controller 的实例,而 Controller 也持有了 View 的实例,这形成了循环依赖 (Controller <-> View)。虽然在很多 MVC 实现中这很常见,但它会增加组件间的耦合度,使得代码更难理解和单独测试。
改进方案: 可以让 View 和 Model 一样,继承自 Eventful,成为一个独立的事件发布者。Controller 则在外部监听 View 的事件,而不是将自身实例注入到 View 中。这样可以形成更单向的数据流:View -> Controller -> Model。

TODOs

  • 结合系统架构师的内容,再细化下流程、每个环节的约束
  • 针对每个模块,增加特定的约束
  • 解决方案库的建设,比如基于/不基于坐标轴的、基于日历的等等,搞成几套固定的方案,然后让AI辅助我们思考和权衡
  • 优化Controller,明确其职责边界
  • test模块

如何快速学习掌握这个框架

  • 用Gemini提问的方式,学习这个框架
  • 用这个框架做一个新需求,或者将一个老的项目用这个框架重构