Appearance
起因
最近想给博客的友链添加一个自动监测功能,能够显示每个友链的在线状态。这样访问者就能知道哪些朋友的网站还能正常访问
实现过程
1. 定义类型接口
首先创建类型定义文件 types/linkStatus.ts
:
typescript
export interface FriendLink {
name: string;
title: string;
avatar: string;
link: string;
status?: LinkStatus;
lastChecked?: Date;
}
export interface LinkStatus {
isOnline: boolean;
statusCode?: number;
responseTime?: number;
error?: string;
}
2. 实现链接检查服务
创建 utils/simpleLinkChecker.ts
:
typescript
export class SimpleLinkChecker {
async checkLinks(links: FriendLink[]): Promise<FriendLink[]> {
const results = await Promise.allSettled(
links.map(link => this.checkSingleLink(link))
);
return links.map((link, index) => {
const result = results[index];
if (result.status === 'fulfilled' && result.value) {
return {
...link,
status: result.value.status,
lastChecked: result.value.timestamp,
};
}
// If failed, mark as offline
return {
...link,
status: {
isOnline: false,
error: result.status === 'rejected' ? result.reason.message : 'Check failed',
},
lastChecked: new Date(),
};
});
}
}
遇到的坑
CORS 问题
第一个大坑是浏览器的 CORS 政策。最初我尝试直接发送 HEAD
请求:
typescript
const response = await fetch(link.link, {
method: 'HEAD',
mode: 'cors',
});
但大多数网站都不会返回 CORS 头,导致请求失败。解决方案是:
- 先尝试
mode: 'cors'
- 如果失败,改用
mode: 'no-cors'
- 作为后备,使用
Image
对象加载
typescript
// Try with ping approach using Image
const img = new Image();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = () => reject(new Error('Image load failed'));
img.src = link.link + '?t=' + Date.now();
});
Vue 列表渲染问题
在实现排序功能时,遇到了一个奇怪的问题:排序后不同的友链显示了相同的头像。这是因为 Vue 的虚拟 DOM 复用机制
问题代码:
vue
<div v-for="(friend, index) in displayLinks" :key="index">
解决方案:
vue
<div v-for="(friend, index) in displayLinks" :key="friend.link">
使用 friend.link
作为 key,确保每个元素都有唯一的标识
缓存策略
为了避免每次页面加载都检查所有链接,实现了缓存机制:
typescript
// Save to localStorage
localStorage.setItem('friendLinksStatus', JSON.stringify({
links: results,
timestamp: Date.now()
}));
// Check if cache is expired (24 hours)
const hoursSinceLastCheck = (now - lastCheck) / (1000 * 60 * 60);
if (hoursSinceLastCheck >= 24) {
this.checkLinksStatus();
}
最终效果
- 自动排序:在线的友链自动排在前面
- 状态显示:右上角显示 "ONLINE" 或 "OFFLINE" 标签
- 每日更新:每 24 小时自动检查一次
- 本地缓存:使用 localStorage 缓存结果