Skip to content

Commit c3df72d

Browse files
committed
优化图标加载失败时的显示
1 parent 588c50c commit c3df72d

File tree

8 files changed

+724
-60
lines changed

8 files changed

+724
-60
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# 图标加载失败处理演示
2+
3+
## 快速测试方法
4+
5+
### 方法 1:使用浏览器开发者工具
6+
7+
1. 打开网站
8+
2. 按 F12 打开开发者工具
9+
3. 切换到 Console 标签
10+
4. 运行以下代码添加测试链接:
11+
12+
```javascript
13+
// 测试 1:无效的自定义图标 URL
14+
const testLink1 = {
15+
id: 'test-1',
16+
name: '测试:无效图标',
17+
url: 'https://github.com',
18+
description: '这个链接使用了无效的图标URL',
19+
category: '主页',
20+
icon: 'https://invalid-domain-12345.com/icon.png',
21+
backgroundColor: '#1890ff',
22+
iconScale: 0.7,
23+
tags: [],
24+
order: 0,
25+
createdAt: Date.now(),
26+
updatedAt: Date.now()
27+
};
28+
29+
// 测试 2:无效的域名(favicon 也会失败)
30+
const testLink2 = {
31+
id: 'test-2',
32+
name: '测试:完全失败',
33+
url: 'https://invalid-domain-99999.com',
34+
description: '这个链接的图标和favicon都会失败',
35+
category: '主页',
36+
icon: 'https://invalid-domain-12345.com/icon.png',
37+
backgroundColor: '#52c41a',
38+
iconScale: 0.7,
39+
tags: [],
40+
order: 0,
41+
createdAt: Date.now(),
42+
updatedAt: Date.now()
43+
};
44+
45+
// 测试 3:只依赖 favicon(应该成功)
46+
const testLink3 = {
47+
id: 'test-3',
48+
name: '测试:Favicon回退',
49+
url: 'https://www.google.com',
50+
description: '这个链接没有自定义图标,应该显示Google的favicon',
51+
category: '主页',
52+
backgroundColor: '#4285f4',
53+
iconScale: 0.7,
54+
tags: [],
55+
order: 0,
56+
createdAt: Date.now(),
57+
updatedAt: Date.now()
58+
};
59+
60+
// 添加到 localStorage
61+
const existingLinks = JSON.parse(localStorage.getItem('navigation-links') || '[]');
62+
existingLinks.push(testLink1, testLink2, testLink3);
63+
localStorage.setItem('navigation-links', JSON.stringify(existingLinks));
64+
65+
// 刷新页面
66+
location.reload();
67+
```
68+
69+
### 方法 2:通过 UI 手动添加
70+
71+
1. 点击页面上的"添加链接"按钮
72+
2. 填写以下信息:
73+
74+
**测试链接 1:无效图标**
75+
- 名称:测试:无效图标
76+
- 地址:https://github.com
77+
- 描述:这个链接使用了无效的图标URL
78+
- 图标:https://invalid-domain-12345.com/icon.png
79+
- 背景色:#1890ff
80+
81+
**测试链接 2:完全失败**
82+
- 名称:测试:完全失败
83+
- 地址:https://invalid-domain-99999.com
84+
- 描述:这个链接的图标和favicon都会失败
85+
- 图标:https://invalid-domain-12345.com/icon.png
86+
- 背景色:#52c41a
87+
88+
**测试链接 3:Favicon回退**
89+
- 名称:测试:Favicon回退
90+
- 地址:https://www.google.com
91+
- 描述:这个链接没有自定义图标,应该显示Google的favicon
92+
- 图标:(留空)
93+
- 背景色:#4285f4
94+
95+
## 预期结果
96+
97+
### 测试链接 1(无效图标)
98+
- ✅ 不显示破损的图片图标
99+
- ✅ 自动回退到 GitHub 的 favicon
100+
- ✅ 控制台显示:`图标加载失败: https://invalid-domain-12345.com/icon.png`
101+
102+
### 测试链接 2(完全失败)
103+
- ✅ 不显示破损的图片图标
104+
- ✅ 显示默认的链接图标(LinkOutlined)
105+
- ✅ 控制台显示两条警告:
106+
- `图标加载失败: https://invalid-domain-12345.com/icon.png`
107+
- `Favicon 加载失败: [favicon URL]`
108+
109+
### 测试链接 3(Favicon回退)
110+
- ✅ 显示 Google 的 favicon
111+
- ✅ 图标清晰,没有破损
112+
113+
## 清理测试数据
114+
115+
测试完成后,可以通过以下方式清理测试数据:
116+
117+
### 方法 1:通过 UI 删除
118+
右键点击测试链接卡片,选择"删除"
119+
120+
### 方法 2:通过控制台清理
121+
```javascript
122+
// 删除所有测试链接
123+
const links = JSON.parse(localStorage.getItem('navigation-links') || '[]');
124+
const cleanedLinks = links.filter(link => !link.name.startsWith('测试:'));
125+
localStorage.setItem('navigation-links', JSON.stringify(cleanedLinks));
126+
location.reload();
127+
```
128+
129+
## 技术说明
130+
131+
### 修复前的问题
132+
```typescript
133+
// 问题代码:使用 display: 'none' 隐藏
134+
<img
135+
src={src}
136+
style={{ display: hasError ? 'none' : 'block' }}
137+
onError={() => setHasError(true)}
138+
/>
139+
```
140+
141+
浏览器可能在设置 `display: 'none'` 之前短暂显示破损图标。
142+
143+
### 修复后的方案
144+
```typescript
145+
// 解决方案:条件渲染,完全不渲染失败的元素
146+
if (hasError && (faviconError || !fallbackUrl)) {
147+
return <DefaultIcon />;
148+
}
149+
150+
if (hasError && fallbackUrl && !faviconError) {
151+
return <img src={fallbackUrl} onError={() => setFaviconError(true)} />;
152+
}
153+
154+
return <img src={src} onError={() => setHasError(true)} />;
155+
```
156+
157+
这样可以确保失败的 `<img>` 元素完全不会被渲染到 DOM 中。
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# 图标加载失败处理测试指南
2+
3+
## 测试目的
4+
5+
验证链接卡片的图标加载失败时,能够正确显示默认图标而不是破损的图片。
6+
7+
## 修复说明
8+
9+
### 问题描述
10+
之前的实现使用 `display: 'none'` 来隐藏加载失败的图片,但浏览器可能仍会短暂显示破损的图片图标。
11+
12+
### 解决方案
13+
改用条件渲染(conditional rendering),根据加载状态完全不渲染失败的 `<img>` 元素,而是直接渲染下一级回退选项。
14+
15+
### 代码变更
16+
- 移除了 `imageLoaded``faviconLoaded` 状态
17+
- 移除了 `display: 'none'` 样式
18+
- 使用条件渲染:只在对应状态下渲染对应的元素
19+
- 简化了逻辑,提高了性能
20+
21+
## 测试步骤
22+
23+
### 1. 测试自定义图标加载失败
24+
25+
**操作:**
26+
1. 打开浏览器开发者工具(F12)
27+
2. 切换到 Network 标签
28+
3. 在 Network 标签中启用 "Offline" 模式或使用 "Block request URL" 功能
29+
4. 添加一个新链接,使用一个无效的图标 URL(例如:`https://invalid-domain-12345.com/icon.png`
30+
5. 观察卡片显示
31+
32+
**预期结果:**
33+
- 不应该看到破损的图片图标
34+
- 应该自动尝试加载该网站的 favicon
35+
- 如果 favicon 也失败,应该显示默认的链接图标(LinkOutlined)
36+
- 控制台应该有警告信息:`图标加载失败: https://invalid-domain-12345.com/icon.png`
37+
38+
### 2. 测试 Favicon 回退
39+
40+
**操作:**
41+
1. 添加一个链接,不设置自定义图标
42+
2. 确保该链接的域名有效(例如:`https://github.com`
43+
3. 观察卡片显示
44+
45+
**预期结果:**
46+
- 应该自动加载 GitHub 的 favicon
47+
- 图标应该清晰显示,没有破损图标
48+
49+
### 3. 测试完全失败的情况
50+
51+
**操作:**
52+
1. 添加一个链接,使用无效的自定义图标 URL
53+
2. 确保该链接的域名也无效或无法访问(例如:`https://invalid-domain-12345.com`
54+
3. 观察卡片显示
55+
56+
**预期结果:**
57+
- 不应该看到任何破损的图片图标
58+
- 应该直接显示默认的 LinkOutlined 图标
59+
- 控制台应该有两条警告信息:
60+
- `图标加载失败: [自定义图标URL]`
61+
- `Favicon 加载失败: [favicon URL]`
62+
63+
### 4. 测试图标大小一致性
64+
65+
**操作:**
66+
1. 创建多个链接:
67+
- 一个使用有效的自定义图标
68+
- 一个使用 favicon
69+
- 一个使用默认图标
70+
2. 观察所有卡片的图标大小
71+
72+
**预期结果:**
73+
- 所有图标的大小应该保持一致
74+
- 默认图标的大小应该与自定义图标相同(受 `iconScale` 属性控制)
75+
76+
## 验证清单
77+
78+
- [ ] 自定义图标加载失败时不显示破损图标
79+
- [ ] 自动回退到 favicon
80+
- [ ] Favicon 失败时自动回退到默认图标
81+
- [ ] 默认图标大小与自定义图标一致
82+
- [ ] 控制台正确记录警告信息
83+
- [ ] 没有闪烁或视觉跳动
84+
- [ ] 图标过渡平滑自然
85+
86+
## 相关文件
87+
88+
- `components/navigation/LinkCard.tsx` - 主要修复文件
89+
- `.kiro/specs/frontend-navigation-site/requirements.md` - 需求 11
90+
- `.kiro/specs/frontend-navigation-site/design.md` - LinkCard 组件设计
91+
92+
## 技术细节
93+
94+
### IconWithFallback 组件状态管理
95+
96+
```typescript
97+
const [hasError, setHasError] = useState(false); // 自定义图标是否失败
98+
const [faviconError, setFaviconError] = useState(false); // Favicon 是否失败
99+
```
100+
101+
### 渲染逻辑
102+
103+
1. **初始状态**:渲染自定义图标
104+
2. **自定义图标失败**`hasError = true`,渲染 favicon
105+
3. **Favicon 失败**`faviconError = true`,渲染默认图标
106+
4. **无 fallbackUrl**:直接从自定义图标失败跳到默认图标
107+
108+
### 性能优化
109+
110+
- 移除了不必要的状态变量
111+
- 减少了 DOM 元素数量(不再同时渲染多个隐藏的 img 元素)
112+
- 简化了条件判断逻辑

0 commit comments

Comments
 (0)