欢迎光临
我们一直在努力

HarmonyOS PC 焦点系统的正确建模方式

在这里插入图片描述 在这里插入图片描述

子玥酱
(掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、CSDN、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出

我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨ 👋 如果你正在做前端,或准备长期走前端这条路 📚 关注我,第一时间获取前端行业趋势与实践总结 🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构) 💡 一起把技术学“明白”,也用“到位”

持续写作,持续进阶。 愿我们都能在代码和生活里,走得更稳一点 🌱

文章目录

    • 引言
    • 一个必须先承认的事实
    • 焦点 ≠ 高亮
    • PC 焦点的本质:输入路由权
    • 高频坑:组件各自抢焦点
    • 正确思路:焦点必须集中建模
    • 一、定义一个明确的 FocusModel
    • 二、组件只声明“我能不能被聚焦”
    • 三、焦点切换由 Controller 统一调度
    • 四、键盘事件只看 FocusModel
      • 错误做法:组件自己处理键盘
      • 正确做法:集中分发
    • 五、Tab / 方向键:不是 UI 行为,而是焦点策略
      • 正确方式:焦点顺序也是模型
    • 六、多窗口场景:焦点必须绑定 Workspace
    • 为什么焦点问题这么“折磨人”?
    • 一个快速自检清单
    • 总结

引言

如果你已经把 HarmonyOS 应用做到 PC 形态,大概率迟早会遇到这些问题:

Tab 键乱跳,焦点有时消失,鼠标点了,键盘却没反应 多窗口一切换,输入全失效

第一反应通常是:

是不是系统焦点机制太复杂?

于是你开始:

  • 强行 requestFocus
  • 到处打 log
  • 在组件生命周期里补焦点
  • 写一堆兜底逻辑

但越补越乱,因为真正的问题,并不在“焦点 API”。

一个必须先承认的事实

在 PC 场景下:

焦点不是 UI 状态,而是一种“交互所有权”。

而大多数项目,一开始就把它当成了:

“当前哪个组件高亮了”。

这一步,就已经走偏了。

焦点 ≠ 高亮

很多代码,焦点逻辑是这样写的:

@State isFocused: boolean = false

onFocus() {
this.isFocused = true
}

onBlur() {
this.isFocused = false
}

然后所有行为都基于这个状态判断。

问题在于:

  • 高亮只是表现,焦点却决定 输入去向

当你把两者绑死时:

视觉没问题,交互已经乱了。

PC 焦点的本质:输入路由权

在 HarmonyOS PC 下,焦点至少决定三件事:

  • 键盘事件发给谁
  • 快捷键是否生效
  • 输入法是否激活
  • 但很多项目,焦点分散在各个组件里:

    TextInput {
    onFocus() { /* … */ }
    }

    List {
    onFocus() { /* … */ }
    }

    结果就是:

    没有任何地方,知道“现在谁真正拥有输入”。

    高频坑:组件各自抢焦点

    你一定见过这种写法:

    onClick() {
    this.requestFocus()
    }

    onAppear() {
    this.requestFocus()
    }

    短期看能解决问题,长期看是灾难:

    • 多个组件同时请求
    • 窗口切换时反复触发
    • 焦点状态不可预测

    最终表现出来的就是:

    焦点像在“漂移”。

    正确思路:焦点必须集中建模

    在 PC 项目中,有且只能有一个地方回答这个问题:

    “当前输入属于谁?”

    我们把它单独建模。

    一、定义一个明确的 FocusModel

    // pc/focus/FocusModel.ts
    export class FocusModel {
    private focusedId?: string

    focus(id: string) {
    this.focusedId = id
    }

    blur(id: string) {
    if (this.focusedId === id) {
    this.focusedId = undefined
    }
    }

    isFocused(id: string): boolean {
    return this.focusedId === id
    }
    }

    注意这里的关键点:

    • 焦点不是组件实例
    • 而是一个稳定的标识
    • UI 只是“注册者”

    二、组件只声明“我能不能被聚焦”

    // pc/focus/Focusable.ts
    export interface Focusable {
    id: string
    canFocus(): boolean
    }

    class EditorView implements Focusable {
    id = 'editor'

    canFocus() {
    return true
    }
    }

    组件不主动抢焦点,只声明能力。

    三、焦点切换由 Controller 统一调度

    // pc/focus/FocusController.ts
    export class FocusController {
    constructor(private focusModel: FocusModel) {}

    requestFocus(target: Focusable) {
    if (target.canFocus()) {
    this.focusModel.focus(target.id)
    }
    }
    }

    现在:

    • 点击
    • Tab
    • 窗口激活

    全部走同一条路径。

    四、键盘事件只看 FocusModel

    这是最容易被忽略、但最关键的一步。

    错误做法:组件自己处理键盘

    onKeyDown(e) {
    if (this.isFocused) {
    handleKey(e)
    }
    }

    正确做法:集中分发

    function onKeyDown(e) {
    const focusedId = focusModel.current()
    dispatchKeyEvent(focusedId, e)
    }

    function dispatchKeyEvent(id: string, e) {
    const handler = registry.get(id)
    handler?.onKey(e)
    }

    焦点决定“谁接收”,不是“谁自己判断”。

    五、Tab / 方向键:不是 UI 行为,而是焦点策略

    很多项目会这样写:

    onTab() {
    focusNext()
    }

    问题是:

    • “下一个”是谁?
    • 顺序在哪里定义?
    • 不同窗口是否一致?

    正确方式:焦点顺序也是模型

    class FocusOrder {
    private order: string[] = []

    next(current: string): string | undefined {
    const index = this.order.indexOf(current)
    return this.order[index + 1]
    }
    }

    onTab() {
    const next = focusOrder.next(focusModel.current())
    if (next) focusModel.focus(next)
    }

    这样你才能:

    • 控制 Tab 行为
    • 做无障碍支持
    • 支持键盘优先模式

    六、多窗口场景:焦点必须绑定 Workspace

    在 PC 上,焦点不是全局唯一的。

    class WorkspaceFocus {
    workspaceId: string
    focusModel: FocusModel
    }

    窗口切换时:

    • Workspace A 的焦点被冻结
    • Workspace B 的焦点恢复

    否则你一定会遇到:

    在 A 窗口打字,却改了 B 的内容。

    为什么焦点问题这么“折磨人”?

    因为:

    • 错乱立刻体现在输入上
    • 键盘问题比渲染更明显
    • 用户会直接觉得“不能用”

    但根因往往是:

    你从来没有一个地方,真正定义过“焦点是什么”。

    一个快速自检清单

    如果你的 HarmonyOS PC 项目:

    • 在组件里频繁 requestFocus
    • 焦点状态分散在 UI State
    • 键盘事件由组件自己判断
    • Tab 行为写在页面逻辑里

    那几乎可以确定:

    焦点模型缺失。

    总结

    在 HarmonyOS PC 上,焦点不是一个 UI 技巧,而是一种输入资源的分配机制。

    • 模型不集中,焦点必乱
    • 焦点不稳定,交互必崩
    • 焦点一乱,多输入全废
    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS PC 焦点系统的正确建模方式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址