为了提升用户体验,我希望在文章页顶部添加一个天气显示功能。这个功能不仅能让读者了解当地的天气状况,还能增加博客的互动性和实用性。然而,实现这个功能面临着几个挑战:
- API稳定性问题:单一API可能会出现故障或响应缓慢
- API额度限制:免费API通常有请求次数限制
- 定位准确度:不同设备和IP定位服务的准确度差异较大
- 城市名称显示:确保显示真实的城市名称,而不是模糊的"当前位置"
- 页面加载速度:天气功能不能拖慢页面加载速度
为了解决这些问题,我提出了需求然后让Trea Ai设计并实现了一个具有多API fallback机制的天气显示功能,并进行了多次优化。
核心功能:
- ✅ 自动获取用户位置,显示当地天气信息
- ✅ 多API fallback机制,确保天气数据的可靠性
- ✅ 显示真实的城市名称,提升用户体验
- ✅ 现代化的卡片设计,适配深色模式
- ✅ 响应式布局,适配不同设备
- ✅ 缓存机制,减少API请求,提高页面加载速度
- ✅ 请求超时处理,避免页面长时间等待
技术特点:
- 多API集成:集成了多个IP定位和天气API服务
- 智能fallback机制:并行请求多个API,取最快返回的结果
- 高精度定位:优先使用国内地图服务,提高国内用户定位准确度(为了用户隐私问题,就没有设计访问用户设备的GPS信息,不然访问GPS信息定位是最准确的)
- 现代化UI设计:渐变背景、平滑动画、响应式布局
- 可扩展性:代码结构清晰,易于扩展和维护
架构设计

核心代码实现
地理位置API配置:
```javascript
// 地理位置API配置
const geolocationAPIs = [
// 第一个IP定位API: ip-api.com
async () => {
const response = await fetchWithTimeout('http://ip-api.com/json?lang=zh-CN&fields=status,message,lat,lon,country,regionName,city');
const data = await response.json();
return {
lat: data.lat,
lon: data.lon,
city: data.city,
regionName: data.regionName
};
},
// 第二个IP定位API: ipinfo.io
async () => {
const response = await fetchWithTimeout('https://ipinfo.io/json');
const data = await response.json();
const [lat, lon] = data.loc.split(',');
return {
lat: parseFloat(lat),
lon: parseFloat(lon),
city: data.city,
regionName: data.region || ''
};
},
// 第三个IP定位API: freegeoip.app
async () => {
const response = await fetchWithTimeout('https://freegeoip.app/json/');
const data = await response.json();
return {
lat: data.latitude,
lon: data.longitude,
city: data.city,
regionName: data.region_name || ''
};
}
];
```
天气API配置:
```javascript
// 天气API配置
const weatherAPIs = [
// 第一个API: Open-Meteo (免费,不需要密钥)
async (lat, lon) => {
const response = await fetchWithTimeout(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code&forecast_days=1&timezone=Asia/Shanghai`);
const data = await response.json();
// 天气代码映射
const weatherCodes = {
0: '晴', 1: '晴', 2: '多云', 3: '阴',
45: '雾', 48: '雾',
51: '小雨', 53: '小雨', 55: '小雨',
61: '中雨', 63: '中雨', 65: '大雨',
71: '小雪', 73: '小雪', 75: '大雪',
80: '阵雨', 81: '阵雨', 82: '阵雨',
95: '雷暴', 96: '雷暴', 99: '雷暴'
};
const description = weatherCodes[data.current.weather_code] || '未知';
return {
city: '', // 留空,让主流程使用IP定位的城市名称
temperature: Math.round(data.current.temperature_2m),
description: description,
humidity: data.current.relative_humidity_2m,
windSpeed: Math.round(data.current.wind_speed_10m),
icon: getWeatherIconByCondition(description)
};
},
// 其他天气API配置...
];
```
并行API调用实现:
```javascript
// 并行尝试API,使用最快返回的结果
async function tryAPIsParallel(apiList, ...args) {
return new Promise((resolve, reject) => {
let completed = false;
let failureCount = 0;
// 创建所有API请求
apiList.forEach((api, index) => {
api(...args)
.then(result => {
if (!completed) {
completed = true;
resolve(result);
}
})
.catch(error => {
failureCount++;
if (failureCount === apiList.length && !completed) {
completed = true;
reject(new Error('所有API都调用失败'));
}
});
});
});
}
```
城市名称处理逻辑:
```javascript
// 主流程中的城市名称处理
const location = await tryAPIsSequential(geolocationAPIs);
const weather = await tryAPIsParallel(weatherAPIs, location.lat, location.lon);
// 优先使用IP定位获取的城市名称
if (location.city) {
weather.city = location.city;
} else if (location.regionName) {
weather.city = location.regionName;
} else if (weather.city && weather.city !== '当前位置' && weather.city !== '') {
// 使用天气API返回的城市名称
} else {
weather.city = '未知城市';
}
```
缓存机制实现:
```javascript
// 缓存配置
const CACHE_KEY = 'weather_cache';
const CACHE_DURATION = 30 * 60 * 1000; // 30分钟缓存
// 从缓存获取数据
function getCache() {
const cache = localStorage.getItem(CACHE_KEY);
if (cache) {
const { data, timestamp } = JSON.parse(cache);
if (Date.now() - timestamp < CACHE_DURATION) {
return data;
}
}
return null;
}
// 保存数据到缓存
function setCache(data) {
localStorage.setItem(CACHE_KEY, JSON.stringify({
data,
timestamp: Date.now()
}));
}
```
遇到的问题和解决方案
1. 单一API故障问题
- 问题:最初只使用了一个天气API,当该API出现故障时,天气功能完全失效。
- 解决方案:实现了多API fallback机制,并行请求多个API,取最快返回的结果。
2. 城市名称显示问题
- 问题:天气API返回的城市名称不准确,显示"当前位置"或错误的城市名称。
- 解决方案:修改了代码逻辑,始终优先使用IP定位获取的城市名称,确保显示真实的城市名称。
3. 页面加载速度问题
- 问题:天气API请求拖慢了页面加载速度。
- 解决方案:添加了缓存机制,减少API请求。实现了请求超时处理,避免页面长时间等待。使用并行API调用,减少等待时间。
4. 定位准确度问题
- 问题:部分IP定位服务的准确度较低,特别是对于国内用户。
- 解决方案:添加了国内地图服务(高德地图、百度地图)。优化了API调用顺序,优先使用国内服务。增加了IP定位服务的数量,提高定位成功率。
优化过程
1. 初始版本
- 单一IP定位和天气API。
- 基本的卡片设计。
- 显示"当前位置"作为城市名称。
2. 多API版本
- 添加了多API fallback机制。
- 优化了UI设计,添加了渐变背景和动画效果。
- 支持深色模式。
3. 定位优化版本
- 添加了国内地图服务,提高国内用户定位准确度。
- 优化了API调用顺序。
- 增加了请求超时处理。
4. 城市名称优化版本
- 修复了城市名称显示问题,显示真实的城市名称。
- 优化了IP定位API的返回数据,确保包含城市名称。
- 调整了天气API的城市名称处理逻辑。
5. 性能优化版本
- 添加了缓存机制,减少API请求。
- 优化了并行API调用逻辑。
- 减少了不必要的API数量,只保留最可靠的服务。
最终效果

代码集成
WordPress主题集成,将代码添加到WordPress主题的`single.php`文件中即可快速集成:
```php
<?php get_header(); ?>
<section class="index_area">
<div class="container">
<div class="row g-4">
<div class="col-lg-8">
<div class="post_container_title">
<h1><?php the_title(); ?></h1>
<!-- 其他标题信息 -->
</div>
<div class="post_container">
<?php while( have_posts() ): the_post(); $p_id = get_the_ID(); ?>
<article class="wznrys">
<!-- 天气显示区域 -->
<div class="weather-container" id="weatherContainer"></div>
<?php the_content(); ?>
</article>
<!-- 天气功能的CSS和JavaScript代码 -->
<style>
/* 天气组件样式 */
.weather-container {
/* 样式代码 */
}
/* 其他样式 */
</style>
<script>
// 天气显示功能的JavaScript代码
function initWeather() {
// 实现代码
}
// 页面加载完成后初始化天气
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initWeather);
} else {
initWeather();
}
</script>
<?php endwhile; ?>
<!-- 其他文章内容 -->
</div>
</div>
<?php get_sidebar(); ?>
</div>
</div>
</section>
<?php get_footer(); ?>
```
如涉及侵权,请通过邮件:gouweicaosheji#163.com与我联系处理。
宗宗酱
看到了,今天还在大佬论坛点进来,原来是你发的啊,哈哈
@Hary 哈哈哈,电击小子就是我
@Hary