Skip to content

perf(frontend): reduce main-thread work while dock dialogs are open - #201

Merged
yyg-max merged 4 commits into
linux-do:masterfrom
Aether-254:perf/dialog-hover-jank
Oct 9, 2026
Merged

yyg-max merged 4 commits into
linux-do:masterfrom
Aether-254:perf/dialog-hover-jank

Conversation

@Aether-254

@Aether-254 Aether-254 commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor
  • Dialog: use a proper controlled/uncontrolled open state instead of mirroring the open prop into local state through an effect, and memoize the context value so consumers stop re-rendering on every parent render.
  • DialogOverlay: promote the backdrop-blur overlay to its own compositor layer so hover-driven dock animations no longer re-blur the whole page.
  • TabsList: observe only data-state changes on direct children instead of every attribute mutation in the subtree, and schedule the initial read in an animation frame.
  • TabsContents / MarkdownEditor: coalesce resize-driven state updates into a single animation frame and cancel it on unmount; skip state updates when the line mapping is unchanged.
  • MotionHighlightItem: skip bounds measurement for inactive items.

例行检查

  • 我已阅读并理解 贡献者公约,
  • 我已阅读并同意 贡献者许可协议 (CLA),确认我的贡献将根据项目的 MIT 许可证进行许可,
  • 我知晓如果此 PR 并不做出实质性更改,或可被认为是为了PR被合并而提交PR的,则可能不会被合并,

变更内容

  • Dialog:改为标准受控/非受控模式,不再用 effect 把 open prop 镜像到本地 state;context value 用 useMemo 固定,避免消费者跟随父组件重渲染。
  • DialogOverlay:加 will-change-transform transform-gpu,让模糊遮罩独立成合成层,dock 动画不再触发整屏重新模糊。
  • TabsList:只监听直接子元素的 data-state 属性,初次读取放到 requestAnimationFrame。
  • TabsContents / MarkdownEditor:resize 触发的 state 更新合并到单个动画帧并在卸载时取消;行映射未变化时跳过 setState。
  • MotionHighlightItem:非激活项不再逐个测量 bounds;高亮块的清空逻辑上移到父组件,仅在没有激活项时触发。

不涉及任何动画参数(时长、曲线、缩放比例均未改动)。

变更原因

打开 dock 上的「快速创建」或「个人信息」弹窗后,即使不做任何点击,鼠标在 dock 上移动时图标缩放动画会明显掉帧。Chrome Performance 录制显示两类开销:

  1. 弹窗遮罩层带 backdrop-blur,dock 每帧缩放都会让整屏重新模糊。
  2. 弹窗内容在父组件每次渲染时都会跟着重渲染(DateTimePicker 内 144 个 SelectItem 乘以两个 picker),TabsList 的 MutationObserver 监听子树所有属性变更,TabsContents 与 MarkdownEditor 用 setTimeout(0) 触发 resize 更新且不做去重。

- Dialog: use a proper controlled/uncontrolled open state instead of
  mirroring the open prop into local state through an effect, and memoize
  the context value so consumers stop re-rendering on every parent render.
- DialogOverlay: promote the backdrop-blur overlay to its own compositor
  layer so hover-driven dock animations no longer re-blur the whole page.
- TabsList: observe only data-state changes on direct children instead of
  every attribute mutation in the subtree, and schedule the initial read
  in an animation frame.
- TabsContents / MarkdownEditor: coalesce resize-driven state updates into a
  single animation frame and cancel it on unmount; skip state updates when
  the line mapping is unchanged.
- MotionHighlightItem: skip bounds measurement for inactive items.
The per-item effect no longer calls clearBounds, so move the reset into the
parent where activeValue is owned. Bounds are cleared only when activeValue
becomes null; switching between items keeps the highlight so it can animate
to the new position.
@Aether-254

Copy link
Copy Markdown
Contributor Author

@yyg-max review 一下 pls

@yyg-max
yyg-max requested a review from chenyme September 22, 2026 15:21
@chenyme
chenyme requested a review from yyg-max September 25, 2026 02:56
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

此 PR 长期无活动,将在 14 天后自动关闭。如需继续讨论请回复

@github-actions github-actions Bot added the Stale label Oct 9, 2026
@yyg-max
yyg-max merged commit b972cdb into linux-do:master Oct 9, 2026
11 checks passed
@Aether-254
Aether-254 deleted the perf/dialog-hover-jank branch October 9, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants