从混沌到有序:揭秘团购鸿蒙高内聚、可扩展的现代化前端架构

引言:每一个复杂页面的背后,都有一场架构的战争

当一个业务页面变得日益复杂时,我们的代码维护工作,就像一场没有硝烟的战争。我们都曾深陷泥潭,与混乱的逻辑、失控的依赖作斗争。本文将分享我们团购业务鸿蒙团队的「破局之路」:如何摒弃传统架构的弊病,设计并实现了一套以「指挥链」为核心的现代化协同作战架构,最终将一个复杂的页面,拆解成了一支战斗力爆表的方面军。

一、起点:所有团队都经历过的「巨石梦魇」

我们先从起点说起,这也是我相信在座各位很多团队都经历过的痛苦:「巨石梦魇」

在项目初期,为了快速迭代,我们最容易写出这样的代码------巨石组件 (God Object Component)。

它长啥样?

  • 一个 .ets 文件,动辄成百上千行。
  • UI 渲染、网络请求、状态管理、用户交互、埋点曝光......所有逻辑都像意大利面条一样搅在一起,密不可分。 让人难以辨清职责边界。

它的痛点,刀刀致命:

  • 牵一发动全身:改个埋点,可能把 UI 搞崩了。
  • 无法测试 :逻辑与 UI 深度 耦合,单元测试无从下手。
  • 新人噩梦:新同学想看懂一个功能,得读完整个文件。
  • 功能瓶颈:像「切 Tab 才曝光」这种依赖外部状态的需求,在这种架构下几乎无法优雅地实现。

二、歧路:看似解耦的「事件总线深渊」

为了解决「巨石」问题,很多团队会引入「事件总线 (Event Bus)」,试图通过它来解耦。

它长啥样?

一个全局的大喇叭。A 组件吼一嗓子,B、C、D 组件听到了就干活。

它带来了新的噩梦:

  • 隐形依赖:代码表面毫无关系,背后全靠「吼」。数据流向完全失控,调试时如同追查幽灵。
  • 命名地狱:事件名是裸字符串,极易冲突,难以管理。
  • 内存泄漏:忘记取消订阅,页面关了,对象还在内存里「偷听广播」

三、破局:我们的「方面军」现代化协同作战架构

我们意识到,必须建立一套全新的秩序。我们的团购页面不是一个孤岛,而是主 App 里的一个重要战区。为了让它既能独立作战,又能和主 App 完美配合,我们设计了这套堪称典范的现代化军事架构------「方面军」架构

这套架构通过清晰的层级划分、精确的职责定义,以及灵活的通信机制 ,将一个复杂的页面,变成了一支高度协同、反应迅速的精英部队。它解决了巨石组件的臃肿,规避了事件总线的混乱,让我们的代码真正实现 内聚 、低 耦合 、可复用、可测试

接下来,我们就深入了解这支「方面军」的每一个成员,看看它们是如何体现我们追求的架构之美与背后的设计原则

第一站:大使馆 & 边防站 (GrouponTabNodeSlot)

  • 他是谁? 主 App(总司令部)派驻在我们战区的「联络官」。
  • 他干啥? 负责「接圣旨」和「发报」,将主框架的命令(如 Tab 切换)翻译成我们内部能听懂的「标准军用电码」(如 PAGE_SHOW),并广播出去。
  • 一句话总结: 连接「中央」和「地方」的桥梁,负责翻译和转发命令。
  • 体现的原则: 它作为一个明确的边界层,体现了职责单一原则( SRP ,仅负责外部事件的接收与内部事件的转换和分发,避免了与业务逻辑的耦合。

第二站:方面军指挥部 (GrouponTabComponent)

  • 他是谁? 整个团购页面的最高指挥官,「大脑中枢」。
  • 他干啥? 负责「建军」(创建 ViewModel、Plugin、Context)、「排兵布阵」(决定页面整体 UI 布局)、「监听电台」(接收上游命令并分发)。
  • 一句话总结: 建制、布局和命令中转的核心,所有部队的直属上级。
  • 体现的原则: 它专注于页面整体的组装和协调,同样遵循了职责单一原则( SRP ,不深入具体的业务逻辑实现,保持了自身的纯粹性。

第三站:多功能战术背包 (GrouponContext)

  • 他是什么? 一个神奇的背包,指挥部会给进入战区的每一支部队都配发一个,里面装满了标准装备。

  • 它包含哪些标准装备

    • 「指挥官电话」 (getHomepageViewModel): 随时联系参谋总长。
    • 「弹药库地图」 (getDataCenter): 知道去哪领共享数据。
    • 「友军频道」 (getScopedInstanceRegistry): 安全地找到其他部队的「大脑」,实现依赖查找。
    • 「技能面板」 (registerAbility): 共享和调用其他部队的特殊能力。
  • 一句话总结: 解耦的灵魂,让各部队既能共享资源,又能互不依赖,极大提升了灵活性和可维护性。

  • 体现的原则: Context 的设计是 ****依赖倒置原则 DIP 的典范。它提供的是抽象接口,而不是具体的实现。这意味着高层模块(例如 ViewModel)不直接依赖低层模块(例如数据中心),而是两者都依赖于 Context 这个抽象。这样,我们可以灵活替换具体的实现,模块间解耦,极大地提升了可测试性。

第四站:参谋总长 (GrouponHomepageViewModel)

  • 他是谁? 「指挥部」任命的首席参谋,所有业务逻辑的「总规划师」。
  • 他干啥? 他直接掌管着所有「精英部队司令」(子 ViewModel)的作战核心,负责制定整体作战计划(如刷新页面数据),并将任务精准分发。他会直接命令各司令更新各自部队的数据。同时,为了避免自身膨胀,对于非 UI 渲染类的跨模块命令(如页面生命周期事件),他会委托「通信部长」(GrouponRootPlugin)进行间接驱动。
  • 一句话总结: 所有 ViewModel 的 ViewModel,业务逻辑的最高聚合点
  • 体现的原则: 参谋总长是职责单一原则( SRP )的体现, 它专注于页面整体的业务逻辑协调和数据分发。同时,它的设计也巧妙地应用了开闭原则( OCP :对于核心的、结构稳定的数据更新,它保持直接调用,效率最高;而对于非数据更新类、可能经常变化的跨模块命令(如生命周期事件),它会委托给 "通信部队" (Plugin) 进行间接驱动。这使得参谋总长的主体代码能对修改关闭 ,而对新增的事件类型能通过 Plugin 机制对扩展开放,避免了自身方法的无限膨胀。

它不直接管理某个具体 UI 组件(部队)的状态,而是管理其他 ViewModel(部队的大脑/司令)。

一个常规的 ViewModel,比如 GrouponDiamondViewModel(金刚位司令),它的职责是管理「金刚位」这个具体组件(部队)的所有事情:数据是什么、什么时候曝光、UI 长什么样等等。它是一个分队司令。

而 GrouponHomepageViewModel(参谋总长),它的级别更高。它手下没有直接的兵(UI 组件),但它直接指挥着所有分队司令,同时通过通信机制协调其行动。

「业务逻辑的最高聚合点」 意思是:所有跨越多个组件的、页面级别的业务逻辑,都在它这里汇总和发起。

举个最典型的例子:下拉刷新整个页面。

命令下达: 用户下拉刷新,这个命令会传到「参谋总长」(GrouponHomepageViewModel) 这里。

制定计划: 「参谋总长」不会自己去画 UI,但它知道完成这个任务需要哪些步骤。它会说:"好的,我需要向总部(服务器)请求最新的全量作战数据。"

分发任务: 等到它从服务器拿回了一大包最新的数据后,它开始分发任务。它会把数据拆开:

  • 对「金刚位司令」(GrouponDiamondViewModel) 说:"这是你的新数据,去更新你的部队!"
  • 对「信息流司令」(MultiTabViewModel) 说:"这是你的新数据,去更新你的部队!"
  • 对「头部司令」(HeaderViewModel) 说:"城市信息变了,更新你的部队!"

你看,它就像一个战役总指挥:

  • 它不关心每个士兵怎么开枪(具体 UI 怎么渲染)。
  • 它只负责发起整个战役(页面刷新),并向各个分队司令直接下达指令(针对数据更新)或通过通信部队间接下达指令(针对其他事件)。
  • 所有需要多个部队协同作战的复杂任务,都在它这里统一协调。

第五站:通信部队 (GrouponRootPlugin & 子插件们)

  • 他们是谁? RootPlugin 是通信部长,手下有一堆负责不同频道的「传令兵」(如GrouponDiamondPlugin)。
  • 他们干啥? 实现专业的「发布-订阅」模式,当"参谋总长"或其他模块有特定命令 (如页面生命周期事件)需要分发时,会通知通信部长。通信部长再将命令精准分发给关心该命令的传令兵 (子插件)。传令兵还会将接收到的指令,传递给对应模块的司令( ViewModel ),或者直接委托给其内部的专业能力代理(Ability)进行后续处理。
  • 一句话总结: 专业的事件分发系统,让指挥部不再关心命令的具体接收方,同时也是能力路由的关键通路。
  • 体现的原则: Plugin 机制是开闭原则( OCP 的典型应用。新增一种事件类型或一个关心此事件的模块,我们只需要增加新的 Plugin 或少量配置,而不需要修改核心的 RootPlugin 逻辑,对扩展开放,对修改关闭,使得系统极易扩展。它也体现了高内聚低耦合,将通信逻辑封装在一起,与业务逻辑解耦。

第六站:集团军司令 (XModeleViewModel)

  • 他是谁? XX 集团军的司令,是整个 XXX 模块的状态管理者命令接收者

  • 他干啥? 他有两个主要职责:

    • 直接接收 来自「参谋总长」的数据更新命令,管理自己模块的数据和 UI 状态。
    • 接收 来自「传令兵」(Plugin)的其他命令(例如「tab 切换」)。
  • 一句话总结: 各模块的门面,负责接收外部指令并委托专业能力进行决策。

  • 体现的原则: 「司令」收到报告后,并不会立刻行动。这正是职责单一原则( SRP 的实践。他专注于管理自己模块的数据和 UI 状态,将复杂的业务决策(如曝光判断)委托给更专业的 Ability,从而保持自身的纯粹性,让代码更易于理解和测试。

第七站:战术专家/能力代理 (XXXAbility)

  • 他是谁? 分区司令任命的**「战术专家」**。一个集团可能有多个「战术专家」,他们拥有处理特定业务逻辑的专业能力。
  • 他干啥? 比如金刚位决策专家 DiamondLynxCardAbility ,他是真正进行曝光决策的模块 。他会综合所有必要的「情报」(这些情报可能来源于 ViewModel 提供的 UI 可见性状态、Lynx 引擎是否就绪、曝光规则优先级等),并根据这些信息,最终决定是否执行曝光。他负责封装复杂的业务决策逻辑。
  • 一句话总结: 封装业务决策逻辑的"大脑",根据各项条件决定是否执行命令。
  • 体现的原则: Ability 的引入是职责单一原则( SRP )的完美体现。 它将复杂的、非 UI 相关的业务逻辑(如曝光判定)从 ViewModel 中彻底剥离出来,让 ViewModel 能够更加专注于 UI 状态管理。当有新的曝光逻辑或判定规则时,我们只需修改或新增对应的 Ability,这完美符合开闭原则(OCP),对扩展开放,对修改关闭。

第八站:通信官 (GrouponAnnieXComponentViewModel)

这里举例金刚位卡片容器,其他类比

  • 他是谁? 下属某个营级单位的专职军官。一个金刚位里可能有多张 Lynx 卡片,每一张卡片(一个营)都配有自己独立的「通信官」。

  • 他干啥? 他的工作是技术性、战术性的。他接收来自「战术专家」(DiamondLynxCardAbility)的具体命令,比如「执行曝光」。

    • 他接到命令后,不会再做决策,而是立刻去检查自己这个营的通信设备------也就是 Lynx 引擎------是否已经架设完毕 (isLynxReady)。
    • 如果设备好了,他立刻通过电台把命令发给前线士兵(Lynx 前端)。
    • 如果设备没好,他会把命令记在小本本上(pendingEvents),等设备一好,马上发送。
  • 特点: 他不关心「为什么」要曝光,只关心「如何」把曝光这个命令在技术层面安全、可靠地发送出去。

  • 体现的原则: 这同样是 职责单一原则( SRP 的体现。通信官只负责命令的最终技术执行和可靠性保障,将 Lynx 相关的技术细节封装在自己内部,与业务决策和 UI 状态管理完全解耦。

架构优势:高内聚、低耦合、可复用、可测试

  • 指挥链清晰: 事件流向单向、可预测,调试易如反掌。
  • 职责分离 Plugin 传话,ViewModel 管理状态,Ability 决策,Component 渲染,各司其职,每个模块都遵循了职责单一原则
  • 依赖 解耦 通过 Context 实现依赖查找,而非直接创建,可测试性拉满,完美体现依赖倒置原则
  • 可扩展性强: 新增功能只需添加新的「部队」和「大脑」,对现有代码零侵入,完美符合开闭原则

四:实战剖析:一次"切 Tab 曝光"的完整旅程

需求背景:

  • 用户切换 Tab、主动刷新后,需上报曝光埋点。
  • 常规的上下滑动不重复上报,进入二级页/后台返回等场景亦不上报。

下面,我们沿着指挥链,看看一次「切 Tab」事件,代码是如何一步步执行的。

第 1 步:边防站(GrouponTabNodeSlot)点燃烽火

根据主框架文档, 可以通过给主框架事件中心注册监听收到指令。GrouponTabNodeSlot作为与主框架的接口,通过监听框架事件,将外部变化转化为内部的标准化生命周期事件。

typescript 复制代码
@Component
export struct GrouponTabNodeSlot {
  @State private lifecycleController: GrouponLifecycleController = new GrouponLifecycleController();

  private mainStructureEventCallback: MainStructureEventCallback = (eventType, param) => {
    let grouponEventType: GrouponLifecycleEventType | null = null;
    let sourceType: SourceReasonType = SourceReasonType.NORMAL;
    // ... 此处省略事件类型转换逻辑 ...
    if (param?.isTabChange) {
      sourceType = SourceReasonType.TOP_TAB
    }
    switch (eventType) {
      case PageEventType.PageShow:
        grouponEventType = GrouponLifecycleEventType.PAGE_SHOW;
        break;
      // ...
    }

    if (grouponEventType !== null) {
      // 通过控制器,将标准化的事件广播出去
      this.lifecycleController.dispatch(grouponEventType, { sourceType: sourceType });
    }
  }

  aboutToAppear(): void {
    // 注册监听
    MainStructureManagerCenter.getInstance().getMainStructureEventManager().registerEvent(HomePage_Groupon, this.mainStructureEventCallback);
  }
  
  // ...
  
  @Builder
  buildGrouponComponent() {
    // 将控制器传递给指挥部
    GrouponTabComponent({
      lifecycleController: this.lifecycleController,
    })
  }
}

第 2 步:指挥部 (GrouponTabComponent) 接收信号,分派任务

指挥部接收到信号后,立刻命令「通信部长」GrouponRootPlugin 将指令传达下去。

typescript 复制代码
@Component
export struct GrouponTabComponent {
  private rootPlugin: GrouponRootPlugin | null = null;
  @Provide grouponContext?: GrouponContextImpl = undefined
  @ObjectLink lifecycleController: GrouponLifecycleController;

  private handleLifecycleChange(eventType: GrouponLifecycleEventType, params: GrouponLifecycleParams): void {
    // 命令通信部长,向下分发事件
    this.rootPlugin?.onLifecycleChanged(eventType, params)
  }

  aboutToAppear(): void {
    this.rootPlugin = new GrouponRootPlugin(this.grouponContext);
    // 设置监听,接收来自边防站的信号
    this.lifecycleController.setListener((eventType, params) => this.handleLifecycleChange(eventType, params));
  }
  // ...
}

第 3 步:通信部长 (GrouponRootPlugin) 接收并分发,传令兵 (GrouponDiamondPlugin) 精准传达

通信部长 会根据事件类型,将命令分发给所有注册的传令兵(子插件)。

传令兵 (GrouponDiamondPlugin) 收到广播后,会判断自己是否关心这个事件。如果关心,它会立刻通知对应的金刚位司令(GrouponDiamondViewModel),告知其接收到了页面生命周期的变化。

  • 级别: 集团军参谋部级别。
  • 职责: 他的工作非常纯粹,就是传递命令。他站在指挥部外面,竖着耳朵听「战区总指挥」(页面生命周期)的广播。
  • 当听到「全体注意,页面出现了!」这个宏观指令时,他会立刻跑去向他的直属领导 ------「金刚位集团军司令」GrouponDiamondViewModel 报告:"报告司令,上级命令,页面可见了!"
  • 特点: 他不关心命令的具体内容是什么,也不关心司令听完报告后会做出什么决策。他只负责把 A 点的消息,准确无误地传到 B 点。

金刚位专属的「传令兵」收到广播后,立刻向自己的直属领导------「金刚位司令」报告。

csharp 复制代码
// 这是在 GrouponRootPlugin 中发生的
this.plugins.forEach(plugin => plugin.onLifecycleChanged(eventType, params));

// 这是在 GrouponDiamondPlugin 中发生的
public onLifecycleChanged(eventType: GrouponLifecycleEventType, params: GrouponLifecycleParams): void {
  // 监听到特定事件
  if (eventType === GrouponLifecycleEventType.PAGE_SHOW && params.sourceType === SourceReasonType.TOP_TAB) {
    // 通过 Context 找到司令,并调用其接口
    this.diamondViewModel.triggerExposureCheck({ reason: 'tab_switch', show: true });
  }
}

第 4 步:金刚位司令 (GrouponDiamondViewModel) 接收命令,委托决策

  • 级别: 金刚位集团军的司令。
  • 职责: 他是金刚位模块的状态管理者命令接收者 。他接收来自"传令兵"的命令(例如"页面可见"),但他太忙了,完全不能抽身直接做最终的曝光决策 。他会将这些业务指令和必要的状态 (如自身可见性、数据是否加载等)传递给金刚位卡片能力代理(DiamondLynxCardAbility) ,由其统一进行决策。
  • 一句话总结:金刚位模块的门面,负责接收外部指令并委托专业能力进行决策

「司令」收到报告后,并不会立刻行动,为遵循单一职责原则,他会召集手下得力干将「专人做专事」。此次警报为曝光警报,于是直接找到分管曝光事件的高级将领让其汇报决策。

typescript 复制代码
// 入口:接收指令
public triggerExposureCheck(params: ExposureTrackParams): void {
  hilog.info(0x0000, 'DiamondExposure', `🔑 收到生命周期事件: ${params.reason}`);
  // 将指令委托给能力代理进行决策
  this.diamondAbilities.forEach((ability: DiamondLynxCardAbility) =>
    ability.triggerExposureCheck({ reason: 'tab_switch', show: true })
  );
}

第 5 步:战术专家/能力代理 (DiamondLynxCardAbility)运筹帷幄,做出决策

  • 级别: 金刚位司令任命的「战术专家」。一个金刚位里可能有多张 Lynx 卡片,每张卡片都对应一个 DiamondLynxCardAbility。他拥有处理特定业务逻辑的专业能力。
  • 职责: 他是真正进行曝光决策的模块。他会综合所有必要的"情报"(这些情报可能来源于 ViewModel 提供的 UI 可见性状态、Lynx 引擎是否就绪、组件是否在屏幕上可见、曝光规则优先级等),并根据这些信息,最终决定是否执行曝光。他负责封装复杂的业务决策逻辑。
  • 一句话总结:封装 业务决策逻辑的"大脑",根据各项条件决定是否执行命令。

「战术专家」收到报告后,并不会立刻行动,而是根据自己掌握的全部信息(是否可见、内容是否就绪等)进入决策中枢进行判断。

kotlin 复制代码
// 这是在 DiamondLynxCardAbility 中发生的决策逻辑
// Component 调用可见性
public updateExposureVisibility(isVisible: boolean): void {
  const wasVisible = this.isCurrentlyVisible;
  this.isCurrentlyVisible = isVisible;

  if (this.isCurrentlyVisible && !wasVisible) {
    hilog.info(0x0000, 'DiamondExposure', `🔑 组件变为可见`);
    this.checkAndTriggerExposure({
      reason: 'visible',
      show: true,
    });
  }
}

// 入口:接收指令
  public triggerExposureCheck(params: ExposureTrackParams): void {
    hilog.info(0x0000, 'DiamondExposure', `🔑 收到生命周期事件: ${params.reason}`);
    this.checkAndTriggerExposure(params);
  }

private checkAndTriggerExposure(params: ExposureTrackParams): void {
  // 1. 处理"离开"事件 (show: false)
  // 离开事件只需要Lynx就绪即可发送,不受组件是否可见的限制。
  if (!params.show) {
    if (params.reason === 'tab_switch' && this.isLynxReady) {
      hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering Tab Switch HIDE exposure!`);
      this.notifyFrontendExposure(params);
    }
    return;
  }

  // 2. 处理"进入"事件 (show: true),必须满足前置条件
  if (!this.isLynxReady || !this.isComponentVisible) {
    hilog.info(0x0000, TAG, `⏳ [${this.getCardIdentifier()}] Exposure condition not met for SHOW event (LynxReady: ${this.isLynxReady}, Visible: ${this.isComponentVisible})`);
    return;
  }

  // 3. 优先处理待处理的刷新事件
  if (this.hasPendingRefresh) {
    hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering PENDING REFRESH exposure!`);
    this.notifyFrontendExposure({ reason: 'user_refresh', show: true });
    this.hasPendingRefresh = false; // 消费掉标志
    this.isFirstShow = false; // 刷新事件消耗首次曝光机会
    return;
  }

  // 4. 处理首次曝光
  if (this.isFirstShow) {
    hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering FIRST EVER exposure! Reason: ${params.reason}`);
    this.notifyFrontendExposure(params);
    this.isFirstShow = false;
    return;
  }

  // 5. 处理后续的"进入"事件,如切Tab回来
  if (params.reason === 'tab_switch') {
    hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering Tab Switch SHOW exposure!`);
    this.notifyFrontendExposure(params);
  }
}
// ... existing code ...

第 6 步:通信官 (GrouponAnnieXComponentViewModel) 确保命令最终执行

  • 级别: 下属某个营级单位的专职军官。一个金刚位里可能有多张 Lynx 卡片,每一张卡片(一个营)都配有自己独立的「通信官」。

  • 职责: 他的工作是技术性、战术性的。他接收来自"司令"的具体命令,比如"执行曝光"。

    • 他接到命令后,不会再做决策,而是立刻去检查自己这个营的通信设备------也就是 Lynx 引擎------是否已经架设完毕 (isLynxReady)。
    • 如果设备好了,他立刻通过电台把命令发给前线士兵(Lynx 前端)。
    • 如果设备没好,他会把命令记在小本本上(pendingEvents),等设备一好,马上发送。
  • 特点: 他不关心"为什么"要曝光,只关心"如何"把曝光这个命令在技术层面安全、可靠地发送出去。

战术专家(DiamondLynxCardAbility)的「曝光」命令下达后,由各卡片专属的「通信官」负责与底层 Lynx 引擎进行技术对接,确保命令在正确的时间被可靠地执行

csharp 复制代码
public sendEventToLynx(event: LynxPendingEvent): void {
  // 判断:士兵的装备(Lynx引擎)好了吗?
  if (this.isLynxReady) {
    // 好了,立刻发送
    this.lynxComponentDelegate?.sendEvent(event.eventName, event.params)
  } else {
    // 没好,先记在小本本上(放入待处理邮箱)
    this.pendingEvents.push(event)
  }
}

public onLynxLoadSuccess(): void {
  // 士兵报告装备完毕!
  this.isLynxReady = true
  // 清空小本本,把之前记下的命令全部发送出去
  this.pendingEvents.forEach(event => {
    this.sendEventToLynx(event)
  })
  this.pendingEvents = []
}

设计哲学:为什么「通信官」不关心 UI 是否可见,以及「能力代理」的意义?

这正是我们架构设计的单一职责原则 Single Responsibility Principle 关注点分离的完美体现!

  • 金刚位司令 (GrouponDiamondViewModel) :他的核心职责是管理 UI 状态接收来自上层的业务指令 。他知道组件是否可见 (isCurrentlyVisible)、数据是否加载好 (isContentLoaded),并将这些状态同步给战术专家 。他自身不负责复杂的决策逻辑,而是将决策权委托 给更专业的 Ability,从而保持自身的纯粹性和职责聚焦。
  • 战术专家 (DiamondLynxCardAbility) :他是业务决策的"大脑" 。他站在山顶上,拿着望远镜,观察战场态势(结合 ViewModel 提供的 UI 可见性、Lynx 引擎就绪状态等信息),并监听来自"司令"的命令。只有当"天时地利人和"都满足时,他才决定"开火!"。他负责封装复杂的曝光规则和优先级判断。
  • 通信官 (GrouponAnnieXComponentViewModel) :他的职责是负责执行。他待在战壕里,不关心外面是否打雷下雨(UI 是否可见),也不关心"为什么"要曝光。他的唯一任务就是维护好和单个士兵的通信线路(与 Lynx 引擎的连接)。当"战术专家"的"开火"命令传来时,他要保证能把这个命令准确地传给士兵。如果士兵的枪还没组装好(Lynx 未就绪),他就先把命令记在小本本上,等士兵一弄好,立刻传达。

这种分层和职责划分,让每个模块都更纯粹、更内聚,也更易于测试和维护。Ability 的引入,使得复杂的业务决策逻辑可以从 ViewModel 中抽离,变成一个独立的、可复用的能力,进一步提升了架构的灵活性和可扩展性

五、结语:架构的价值,在于赋能未来

通过这套精密的架构,我们成功地将一个复杂的团购页面,拆解成了一系列高内聚、低耦合、可复用、可测试的独立作战单元。每个单元各司其职,通过标准化的接口和上下文高效协同。这不仅解决了当下的业务痛点,更重要的是,它为我们未来的快速迭代和功能扩展,打下了坚实而优雅的基础。这,就是技术的价值所在。

相关推荐
zhanshuo11 小时前
鸿蒙开发实战:分布式数据一致性机制详解 + 示例代码
harmonyos
zhanshuo11 小时前
分布式到底有啥用?鸿蒙在工业场景的三个实战告诉你答案
harmonyos
小雨青年13 小时前
HarmonyOS 生态与版本演进
华为·harmonyos
长弓三石20 小时前
鸿蒙网络编程系列59-仓颉版TLS回声服务器示例
harmonyos·鸿蒙·tls·仓颉
yrjw1 天前
一款基于 ReactNative 最新发布的`Android/iOS` 新架构文档预览开源库
harmonyos
ajassi20001 天前
开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放
linux·华为·开源·harmonyos
zhanshuo1 天前
让鸿蒙应用丝滑如飞:绘图性能优化全攻略(附代码实战)
harmonyos
zhanshuo1 天前
适配鸿蒙低性能设备的终极优化方案:从启动到渲染全链路实战
harmonyos
Georgewu1 天前
【HarmonyOS】鸿蒙ArkWeb加载优化方案详解
harmonyos