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

221 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 布局初始化逻辑
<cite>
**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)
</cite>
## 目录
1. [简介](#简介)
2. [核心组件分析](#核心组件分析)
3. [生命周期与容器初始化](#生命周期与容器初始化)
4. [DOM查询机制与containerStr参数](#dom查询机制与containerstr参数)
5. [网格模板初始状态设计](#网格模板初始状态设计)
6. [ResizeObserver响应式尺寸监听](#resizeobserver响应式尺寸监听)
7. [图标重排逻辑](#图标重排逻辑)
8. [依赖关系图](#依赖关系图)
## 简介
`useDesktopContainerInit` 是一个 Vue 3 组合式 API Hook用于在桌面容器组件挂载时初始化其布局系统。该 Hook 负责建立基于 CSS Grid 的动态网格布局,并通过 `ResizeObserver` 实现响应式行为,确保桌面图标能根据容器尺寸自动调整排列方式。
## 核心组件分析
### useDesktopContainerInit 功能概览
此 Hook 封装了桌面容器的核心初始化逻辑,包括:
- 容器元素的 DOM 查询与绑定
- 网格布局参数的初始化与响应式管理
- 容器尺寸变化的监听与处理
- 桌面图标的持久化存储与位置管理
- 图标超出可视区域时的隐藏策略
**Section sources**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L14-L94)
## 生命周期与容器初始化
### onMounted 中的初始化流程
`useDesktopContainerInit` 利用 Vue 的 `onMounted` 生命周期钩子,在组件挂载完成后立即执行容器初始化操作。这一时机确保了对应的 DOM 元素已经存在于页面中,可以安全地进行查询和观察。
`onMounted` 回调中Hook 首先通过 `document.querySelector(containerStr)` 获取指定的容器元素引用,并将其赋值给局部变量 `container`。随后,立即调用 `ResizeObserver``observe` 方法开始监听该容器的尺寸变化。
当组件被卸载时,`onUnmounted` 钩子会确保清理工作正确执行:停止对容器的观察(`unobserve`)并断开 `ResizeObserver` 实例(`disconnect`),防止内存泄漏。
```mermaid
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**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L70-L85)
**Section sources**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L70-L85)
## DOM查询机制与containerStr参数
### containerStr 参数的作用
`containerStr` 是传递给 `useDesktopContainerInit` 函数的一个字符串参数,它代表一个 CSS 选择器。该选择器用于定位需要初始化的桌面容器 DOM 元素。
在当前实现中,`DesktopContainer.vue` 组件传入的值为 `.desktop-icons-container`,这是一个类选择器,指向模板中具有该类名的 `<div>` 元素。这种设计使得 Hook 具有良好的通用性,可以在不同的容器上复用,只需传入相应的选择器即可。
```typescript
// 在 DesktopContainer.vue 中的调用示例
const { appIconsRef, gridStyle, gridTemplate } = useDesktopContainerInit('.desktop-icons-container')
```
**Section sources**
- [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue#L10-L13)
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L14)
## 网格模板初始状态设计
### gridTemplate 初始值的设计意图
`gridTemplate` 对象使用 Vue 的 `reactive` 函数创建,使其成为一个响应式对象。其初始状态的设计体现了以下几个关键考量:
| 属性 | 默认值 | 设计意图 |
|------|--------|----------|
| `cellExpectWidth` | 90 | 预设每个单元格的理想宽度(像素),作为网格列宽的基础 |
| `cellExpectHeight` | 110 | 预设每个单元格的理想高度(像素),作为网格行高的基础 |
| `gapX` / `gapY` | 4 | 设置行列之间的间隙,提供视觉呼吸空间,避免图标紧贴 |
| `colCount` / `rowCount` | 1 | 初始行列数设为1表示最小的网格结构将在首次ResizeObserver回调中被实际尺寸覆盖 |
这些默认值共同定义了一个合理的起始布局,即使在 `ResizeObserver` 回调执行前,也能保证界面有一个基本的、可预测的显示状态。
```mermaid
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**
- [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts#L4-L20)
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L16-L32)
**Section sources**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L16-L32)
- [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts#L4-L20)
## ResizeObserver响应式尺寸监听
### 回调中的尺寸计算逻辑
`ResizeObserver` 的回调函数是实现响应式布局的核心。每当容器的尺寸发生变化时,该回调都会被触发,执行以下关键步骤:
1. **获取容器实际尺寸**:通过 `container.getBoundingClientRect()` 获取容器当前的精确几何信息。
2. **计算行列数量**:利用容器宽度和预设单元格宽度(含间隙)计算出应显示的列数和行数。
```javascript
colCount = Math.floor((width + gapX) / (expectWidth + gapX))
```
3. **计算实际单元格尺寸**:在确定了行列数后,重新分配容器内部空间,计算出每个单元格的实际宽高,确保网格完全填充容器且无多余空白。
```javascript
realWidth = (totalWidth - totalGapX) / colCount
```
这种“期望尺寸 -> 计算行列 -> 反推实际尺寸”的模式,既保证了图标的相对大小一致性,又实现了完美的空间利用率。
**Section sources**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L45-L65)
## 图标重排逻辑
### rearrangeIcons 函数的工作机制
当网格的行列数发生变化时,`watch` 监听器会触发 `rearrangeIcons` 函数,负责重新安排所有桌面图标的可见性和位置。
该函数的主要逻辑如下:
1. 创建一个 `Set` 来记录已被占用的网格坐标 `(x,y)`。
2. 遍历所有图标,优先将位于新网格范围内的图标保留在原位。
3. 对于超出新网格范围的图标,尝试在网格内寻找空闲位置进行安置。
4. 如果网格已满,则将无法放置的图标放入 `hideAppIcons` 数组,这些图标将被隐藏。
这确保了用户界面的连续性,尽可能保留用户的原有布局习惯。
```mermaid
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**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L102-L156)
**Section sources**
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts#L102-L156)
## 依赖关系图
```mermaid
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**
- [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue)
- [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts)
- [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts)