第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定

HarmonyOS 5 一杯冰咖啡 —— MVVM?

標(biāo)簽:
HarmonyOS

一、引言

最近看到一个项目的架构设计,刚开始看还有点懵,但仔细想了想,其实它还挺有意思的。它用了一个混合型架构,表面看像 MVVM,实际上中间加了一层 Controller(或者说是 Presenter/DisPatcher),再加上 Biz 和 Imp,把职责细分得非常明确。今天就借这个机会,跟大家一起聊聊这个架构是怎么设计的,我是怎么理解它的。阿弥陀佛。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,如果你想支持下一期请务必点赞~,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

二、整体结构特点

来一张简图:

image-20250804211508886

名义上说是 MVVM,但其实中间套了一个 Presenter 层(原来命名为 Controller),实质上形成了 View → ViewModel → Presenter → Biz → Imp 的五层结构。从职责来看,每一层都做了该做的事情,但如果不熟悉这种拆法,初看会觉得「层级很多、结构复杂」,但深入理解后,其实是一种在业务复杂度足够高场景下,合理演化出来的结果。

这里简单拆一拆职责:

  • View(UI 层)
    用的是 ComponentV2,拿@Local 接收ViewModel参数,主要负责 UI 渲染和响应交互。不写业务逻辑。状态是从 ViewModel 来的。
  • ViewModel(状态管理层)
    @ObservedV2@Trace 声明响应式字段,View 绑定它,Presenter 来写它。ViewModel 是个纯状态类,基本没逻辑。状态的生命周期跟 UI 走,页面销毁时销毁。
  • Presenter(协调层)
    真正的“控制器”,但不叫 Controller,叫 Presenter或者Dispatcher 更合适。它做两件事:调业务、写状态。生命周期跟 UI 绑定,一般会用 base 类封装生命周期钩子和状态注册。大部分业务逻辑的中间流转(例如请求结果如何反馈到哪个字段)都是在这层完成的。
  • Biz(业务逻辑层)
    这个层负责具体的业务封装,返回的是可观察对象(Observable、Computed、或直接 Promise)。通常还会做一些字段转换、mock 数据填充、失败兜底,甚至是缓存处理。所有 view 不关心的「但又必须做」的业务逻辑,都放这里。
  • Imp(实现层)
    最底层,做网络请求、数据库、本地 IO 的那一层。也就是 data-source 封装层。

注意:这套架构的核心点是 Presenter 负责写 ViewModel,View 只读,Biz 不写 UI 状态,解耦非常清晰。

三、怎么运作的?

说白了,这套架构的本质是「解耦 + 单向流动 + 层级清晰」,我们可以从四个角度来看:

  • 状态流(State Flow)

    来一张简图:

    这部分负责的是 UI 的展示更新。基本原则是:

    • UI 不持状态,所有状态都在 ViewModel;
    • ViewModel 是响应式的,写进去,UI 自动刷新;
    • 只有 Presenter(Controller)能写 ViewModel
    • ViewModel 是纯粹的数据结构,不做业务逻辑
    // ViewModel
    @ObservedV2 class HomeViewModel {
      @State isLoading: boolean = false;
      @State list: Article[] = []
    }
    
    // View
    @Entry
    @ComponentV2
    struct HomePage {
      @Local viewModel = new HomeViewModel()
    
      build() {
        Column() {
          if(this.viewModel.isLoading) {
            Text('加载中...')
          }
          List(this.viewModel.list)
        }
      }
    }
    
    // Presenter
    class HomeController {
      constructor(private vm: HomeViewModel, private biz: HomeBiz) {}
    
      async loadData() {
        this.vm.isLoading = true
        const data = await this.biz.fetchArticles()
        this.vm.list = data
        this.vm.isLoading = false
      }
    }
    
    
  • 事件流(Event Flow)

    1. 正向流

    用户操作 → View → ViewModel → Presenter → Biz → Imp → 网络请求

    2. 逆向流(响应)

    网络响应 → Imp → Biz → Presenter → ViewModel → View(UI自动更新)

    来一张简图:

    image-20250804212716445

    事件流就是用户交互如何传递给业务逻辑。

    • UI 只触发事件(比如 onClick
    • Presenter 响应事件,负责“决定做什么”
    • 再由 Presenter 去调用 Biz 层
    • Biz 层可以异步处理、整合数据、兜底等
    • 最后回到 Presenter,再写入 ViewModel
  • 生命周期联动(Lifecycle Coordination)

    Presenter 和 ViewModel 都是跟随 UI 生命周期存在的。当页面销毁时,会自动释放这些状态和逻辑。(当然了你需要自己绑定生命周期)

    这种结构天然防止了:

    • 定时器/任务泄漏
    • 请求未取消
    • 状态悬挂(UI 销毁后回调才回来)

    此外:

    • 如果需要页面恢复自动刷新数据,可在 onPageShow 中让 Presenter 监听;
    • 如果需要在页面退出时中断请求,可在 onPageHide/onDestroy 中清理 Presenter 内部状态。
  • 跨层交互与回调解耦

    业务层(Biz)永远不会直接改 UI,也不会直接通知 ViewModel,它只暴露标准 Promise 或回调。

    这种方式有几个好处:

    • Presenter 可以自由组合不同业务逻辑(灵活拼装)
    • 可以针对不同 UI 写不同 Presenter,但复用相同 Biz
    • UI 需求变了,不需要改业务逻辑,只改 Presenter 就行
  • 错误处理机制(统一兜底)

    Presenter 是所有请求的入口点,所以也是统一的错误处理中心

    async loadData() {
      try {
        this.vm.isLoading = true
        const data = await this.biz.fetchArticles()
        this.vm.list = data
      } catch (e) {
        this.vm.errorMessage = '网络请求失败'
      } finally {
        this.vm.isLoading = false
    }
    
  • 异步编排能力(支持复杂请求流程)

    多个接口依赖,或需要缓存/合并处理的逻辑,都在 Biz 层做掉,Presenter 不用关心细节。

    // Biz 层
    async fetchArticles(): Promise<Article[]> {
      const cache = this.cacheRepo.load()
      if (cache) return cache
    
      const netData = await this.apiRepo.getArticles()
      this.cacheRepo.save(netData)
      return netData
    }
    

    Presenter 只要管调用:

    const data = await this.biz.fetchArticles()
    
  • 过程代码

第一层:View → ViewModel

@ComponentV2
export struct HomePage {
  @Local viewModel: HomeViewModel = new HomeViewModel()
  controller: HomeController = new HomeController(this.getUIContext(), this.viewModel)
}

第二层:ViewModel → Presenter/Controller

export class HomeController extends BaseController<HomeViewModel> {
  biz: HomeBiz = new HomeBiz()
  viewModel: T  // 持有ViewModel引用
}

第三层:Presenter /Controller→ Biz

getData() {
  const area = areaHistoryUtil.getCurArea()
  this.biz.getData(area.id, this.homeDataCancelRequest).then((result) => {
    if (result) {
      //Do something·
      this.viewModel.recommends = result.recommends
      this.viewModel.lazyMapPres = result.lazyMapPres
      this.viewModel.discoveryPres = result.discoveryPres
    }
  })
}

第四层:Biz → Imp

public async getData(id: string, cancelRequest: CancelRequest): Promise<HomeInfo | undefined> {
  try {
    const homeInfo = await HomeImp.getHomeData(id, cancelRequest)
    return Promise.resolve(UIUtils.makeObserved(homeInfo))
  } catch (err) {
    return Promise.reject(err)
  }
}

第五层:Imp → 网络请求

public static getHomeData(id: string, cancelRequest: CancelRequest): Promise<HomeInfo | undefined> {
  return new Promise((resolve, reject) => {
    Api.get<HomeInfo>(Net.HomeUrl, { "id": id }, cancelRequest).then((result) => {
      resolve(result)
    }).catch((err: BusinessError) => {
      reject(err)
    })
  })
}

四、好在哪?

就像三提到过的,

1. 分工明确,职责单一

  • UI 页面只负责展示和用户操作响应,不涉及任何业务逻辑;
  • ViewModel 是纯状态容器,结构轻,易 mock;
  • Presenter 只做数据流调度、生命周期管理,不关心业务细节;
  • Biz 专注业务封装和组合,不关心 UI 和状态;
  • Imp 是对 SDK / HTTP 的底层封装,适合换实现或写 UT。

职责划分清晰,就意味着每个角色都能独立演进,适合多人协作,尤其适合和 QA、测试团队协作。

2. 易测试、易 mock

  • 想测试 Presenter,可以 mock 掉 Biz 和 VM;
  • 想测试 Biz,可以 mock 掉 Imp;
  • UI 层如果够纯粹,也可以引入自动化 UI 测试。

每层的依赖都是可控制的,利于单测,适合做稳定性要求高的项目。

3. 便于团队协作、可插拔

  • 状态不共享,每一层都是独立封装;
  • VM 之间不直接通信,Presenter 是唯一协调者;
  • 页面状态逻辑清晰,适合模块复用。

换句话说,只要 API 契约稳定,底层怎么实现都能无感替换。

五、它的问题在哪?(缺点分析)

但是吧但是~各位别急着喷,问题当然也是有的!

说了那么多优点,这套架构也并不是万能的,甚至在某些场景下还会“反客为主”,变成一种负担。

1. Presenter(Controller)变成“巨无霸”

虽然试图通过 Presenter 来集中管理数据流和逻辑调度,但实际上,一旦页面稍微复杂一点,Presenter 就极容易变臃肿

常见现象:

  • 写着写着 Presenter 变成了“迷你 ViewModel + 迷你 Biz”的合体;
  • 所有事件、调度逻辑、兜底、节流、节流回调,全都堆在一起;
  • 很难单测,更难复用;
  • 还动不动要关心生命周期、页面状态、异步流程等。

一旦你不小心,就会发现自己维护的是一个 500 行以上的 Controller 文件。

2. ViewModel 只是“数据搬运工”

在这个架构中,ViewModel 被约定成 “纯状态容器”,甚至不能写逻辑(只能读写变量)。这在保持清晰上确实有用,但代价是:

  • ViewModel 的定义量变得很多;
  • 各种字段都要一一声明和维护(比如 loading、empty、error 三个状态);
  • 其实很多状态是临时性的,只为一次交互服务,却也得写进 ViewModel。

如果没有代码生成工具,这会是一件极其重复又机械的活。(好在这个生成工具是好写的)

3. 层次多、理解门槛高,新人上手慢

第一次看这个结构,特别是没有文档的情况下,很容易出现这样的想法:

  • 为什么有 ViewModel 又有 Controller?
  • 为什么状态不直接放在页面里,而要抽出去?
  • 为什么请求不直接写在页面,而要放到 Biz?
  • 为什么请求还得通过 Presenter 走一遍?

也就是说,它比标准 MVVM 增加了一个额外维度的思考成本。一旦团队没有统一规范,新人写出来的 Presenter 和 Biz 很容易搞混甚至交叉。

六、总结一下

“每一层都很清晰,但也意味着每一层都得你写一遍。”

这套架构本质是把职责拆得非常纯粹、单一,但正因为“单一职责”做得太彻底,在某些场景下反而显得不够灵活,甚至拖慢了开发节奏。

尤其是在需求快速变动的前期,你可能会更希望结构简单直接,方便试错和反复修改。

但在一些长线项目、核心页面、多人协作密集的大模块中,这种架构会展现出它的生命力。

结构感越强、可替换性越好、职责边界越清晰,越适合交给团队中的每一个人维护。

愿我们都能找到适合自己项目节奏的架构节奏,阿弥陀佛。

七、结尾

没了。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,如果你想支持下一期请务必点赞~,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

點(diǎn)擊查看更多內(nèi)容
1人點(diǎn)贊

若覺(jué)得本文不錯(cuò),就分享一下吧!

評(píng)論

作者其他優(yōu)質(zhì)文章

正在加載中
感謝您的支持,我會(huì)繼續(xù)努力的~
掃碼打賞,你說(shuō)多少就多少
贊賞金額會(huì)直接到老師賬戶(hù)
支付方式
打開(kāi)微信掃一掃,即可進(jìn)行掃碼打賞哦
今天注冊(cè)有機(jī)會(huì)得

100積分直接送

付費(fèi)專(zhuān)欄免費(fèi)學(xué)

大額優(yōu)惠券免費(fèi)領(lǐng)

立即參與 放棄機(jī)會(huì)
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢(xún)優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)

舉報(bào)

0/150
提交
取消