Files
vue-desktop/.qoder/repowiki/zh/content/响应式布局系统/布局初始化逻辑.md
2025-09-24 16:43:10 +08:00

9.6 KiB
Raw Blame History

布局初始化逻辑

**Referenced Files in This Document** - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts) - [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts)

目录

  1. 简介
  2. 核心组件分析
  3. 生命周期与容器初始化
  4. DOM查询机制与containerStr参数
  5. 网格模板初始状态设计
  6. ResizeObserver响应式尺寸监听
  7. 图标重排逻辑
  8. 依赖关系图

简介

useDesktopContainerInit 是一个 Vue 3 组合式 API Hook用于在桌面容器组件挂载时初始化其布局系统。该 Hook 负责建立基于 CSS Grid 的动态网格布局,并通过 ResizeObserver 实现响应式行为,确保桌面图标能根据容器尺寸自动调整排列方式。

核心组件分析

useDesktopContainerInit 功能概览

此 Hook 封装了桌面容器的核心初始化逻辑,包括:

  • 容器元素的 DOM 查询与绑定
  • 网格布局参数的初始化与响应式管理
  • 容器尺寸变化的监听与处理
  • 桌面图标的持久化存储与位置管理
  • 图标超出可视区域时的隐藏策略

Section sources

生命周期与容器初始化

onMounted 中的初始化流程

useDesktopContainerInit 利用 Vue 的 onMounted 生命周期钩子,在组件挂载完成后立即执行容器初始化操作。这一时机确保了对应的 DOM 元素已经存在于页面中,可以安全地进行查询和观察。

onMounted 回调中Hook 首先通过 document.querySelector(containerStr) 获取指定的容器元素引用,并将其赋值给局部变量 container。随后,立即调用 ResizeObserverobserve 方法开始监听该容器的尺寸变化。

当组件被卸载时,onUnmounted 钩子会确保清理工作正确执行:停止对容器的观察(unobserve)并断开 ResizeObserver 实例(disconnect),防止内存泄漏。

sequenceDiagram
participant Component as "DesktopContainer组件"
participant Hook as "useDesktopContainerInit"
participant DOM as "浏览器DOM"
participant Observer as "ResizeObserver"
Component->>Hook : setup()
Hook->>Hook : 初始化gridTemplate等响应式数据
Component->>Component : 渲染完成
Component->>Hook : onMounted触发
Hook->>DOM : querySelector(containerStr)
DOM-->>Hook : 返回容器元素
Hook->>Observer : new ResizeObserver(callback)
Hook->>Observer : observe(container)
Note over Hook,Observer : 开始监听容器尺寸变化
loop 每次窗口或容器大小改变
Observer->>Observer : 触发回调
Observer->>Hook : 执行回调函数
Hook->>Hook : 重新计算gridTemplate参数
Hook->>Hook : 更新gridStyle
end
Component->>Component : 组件即将销毁
Component->>Hook : onUnmounted触发
Hook->>Observer : unobserve(container)
Hook->>Observer : disconnect()
Note over Hook,Observer : 清理资源,防止内存泄漏

Diagram sources

Section sources

DOM查询机制与containerStr参数

containerStr 参数的作用

containerStr 是传递给 useDesktopContainerInit 函数的一个字符串参数,它代表一个 CSS 选择器。该选择器用于定位需要初始化的桌面容器 DOM 元素。

在当前实现中,DesktopContainer.vue 组件传入的值为 .desktop-icons-container,这是一个类选择器,指向模板中具有该类名的 <div> 元素。这种设计使得 Hook 具有良好的通用性,可以在不同的容器上复用,只需传入相应的选择器即可。

// 在 DesktopContainer.vue 中的调用示例
const { appIconsRef, gridStyle, gridTemplate } = useDesktopContainerInit('.desktop-icons-container')

Section sources

网格模板初始状态设计

gridTemplate 初始值的设计意图

gridTemplate 对象使用 Vue 的 reactive 函数创建,使其成为一个响应式对象。其初始状态的设计体现了以下几个关键考量:

属性 默认值 设计意图
cellExpectWidth 90 预设每个单元格的理想宽度(像素),作为网格列宽的基础
cellExpectHeight 110 预设每个单元格的理想高度(像素),作为网格行高的基础
gapX / gapY 4 设置行列之间的间隙,提供视觉呼吸空间,避免图标紧贴
colCount / rowCount 1 初始行列数设为1表示最小的网格结构将在首次ResizeObserver回调中被实际尺寸覆盖

这些默认值共同定义了一个合理的起始布局,即使在 ResizeObserver 回调执行前,也能保证界面有一个基本的、可预测的显示状态。

classDiagram
class IGridTemplateParams {
+readonly cellExpectWidth : number
+readonly cellExpectHeight : number
+cellRealWidth : number
+cellRealHeight : number
+gapX : number
+gapY : number
+colCount : number
+rowCount : number
}
class useDesktopContainerInit {
-container : HTMLElement
-gridTemplate : IGridTemplateParams
-gridStyle : ComputedRef
-ro : ResizeObserver
+useDesktopContainerInit(containerStr : string) : Object
}
useDesktopContainerInit --> IGridTemplateParams : "包含"

Diagram sources

Section sources

ResizeObserver响应式尺寸监听

回调中的尺寸计算逻辑

ResizeObserver 的回调函数是实现响应式布局的核心。每当容器的尺寸发生变化时,该回调都会被触发,执行以下关键步骤:

  1. 获取容器实际尺寸:通过 container.getBoundingClientRect() 获取容器当前的精确几何信息。
  2. 计算行列数量:利用容器宽度和预设单元格宽度(含间隙)计算出应显示的列数和行数。
    colCount = Math.floor((width + gapX) / (expectWidth + gapX))
    
  3. 计算实际单元格尺寸:在确定了行列数后,重新分配容器内部空间,计算出每个单元格的实际宽高,确保网格完全填充容器且无多余空白。
    realWidth = (totalWidth - totalGapX) / colCount
    

这种“期望尺寸 -> 计算行列 -> 反推实际尺寸”的模式,既保证了图标的相对大小一致性,又实现了完美的空间利用率。

Section sources

图标重排逻辑

rearrangeIcons 函数的工作机制

当网格的行列数发生变化时,watch 监听器会触发 rearrangeIcons 函数,负责重新安排所有桌面图标的可见性和位置。

该函数的主要逻辑如下:

  1. 创建一个 Set 来记录已被占用的网格坐标 (x,y)
  2. 遍历所有图标,优先将位于新网格范围内的图标保留在原位。
  3. 对于超出新网格范围的图标,尝试在网格内寻找空闲位置进行安置。
  4. 如果网格已满,则将无法放置的图标放入 hideAppIcons 数组,这些图标将被隐藏。

这确保了用户界面的连续性,尽可能保留用户的原有布局习惯。

flowchart TD
A[开始重排图标] --> B{遍历所有图标}
B --> C["检查(x,y)是否在maxCol/maxRow范围内"]
C --> |是| D["检查该位置是否已被占用"]
D --> |否| E["将图标加入appIcons数组<br/>标记位置为已占用"]
D --> |是| F["跳过,不添加"]
C --> |否| G["将图标暂存到temp数组"]
G --> H{遍历temp数组}
H --> I["检查appIcons数组是否已满"]
I --> |否| J["在网格中寻找第一个空位"]
J --> K["将图标放入空位<br/>标记位置为已占用"]
K --> L["加入appIcons数组"]
I --> |是| M["加入hideAppIcons数组"]
M --> N[结束]
L --> N
E --> N
F --> N

Diagram sources

Section sources

依赖关系图

graph TB
subgraph "UI层"
DesktopContainerVue["DesktopContainer.vue"]
AppIconVue["AppIcon.vue"]
UseHook["useDesktopContainerInit.ts"]
end
subgraph "类型定义"
IGridTemplate["IGridTemplateParams.ts"]
IDesktopAppIcon["IDesktopAppIcon.ts"]
end
DesktopContainerVue --> UseHook : "调用"
UseHook --> IGridTemplate : "导入接口"
UseHook --> IDesktopAppIcon : "导入接口"
DesktopContainerVue --> AppIconVue : "使用组件"
UseHook --> AppIconVue : "通过返回值传递数据"

Diagram sources