WordPress文章页头部天气显示功能实现与优化

2026-01-04 226 2

为了提升用户体验,我希望在文章页顶部添加一个天气显示功能。这个功能不仅能让读者了解当地的天气状况,还能增加博客的互动性和实用性。然而,实现这个功能面临着几个挑战:
  • API稳定性问题:单一API可能会出现故障或响应缓慢
  • API额度限制:免费API通常有请求次数限制
  • 定位准确度:不同设备和IP定位服务的准确度差异较大
  • 城市名称显示:确保显示真实的城市名称,而不是模糊的"当前位置"
  • 页面加载速度:天气功能不能拖慢页面加载速度

为了解决这些问题,我提出了需求然后让Trea Ai设计并实现了一个具有多API fallback机制的天气显示功能,并进行了多次优化。

核心功能:

  • ✅ 自动获取用户位置,显示当地天气信息
  • ✅ 多API fallback机制,确保天气数据的可靠性
  • ✅ 显示真实的城市名称,提升用户体验
  • ✅ 现代化的卡片设计,适配深色模式
  • ✅ 响应式布局,适配不同设备
  • ✅ 缓存机制,减少API请求,提高页面加载速度
  • ✅ 请求超时处理,避免页面长时间等待

技术特点:

  • 多API集成:集成了多个IP定位和天气API服务
  • 智能fallback机制:并行请求多个API,取最快返回的结果
  • 高精度定位:优先使用国内地图服务,提高国内用户定位准确度(为了用户隐私问题,就没有设计访问用户设备的GPS信息,不然访问GPS信息定位是最准确的)
  • 现代化UI设计:渐变背景、平滑动画、响应式布局
  • 可扩展性:代码结构清晰,易于扩展和维护

架构设计

58ac667823fb6e98cc0b8d2bc4bbea75

核心代码实现

地理位置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}&current=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数量,只保留最可靠的服务。

最终效果

c3152840a9dc29eff4d91c379b89be60

代码集成

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(); ?>
```

免责声明
本站内容(教程、软件、资料等)仅供个人学习研究之用,严禁用于商业或非法目的,使用风险自负。博客部分内容来源自网络,版权归属原作者,本站不承担相关版权责任。请在试用下载后24小时内删除相关资源,支持正版。
如涉及侵权,请通过邮件:gouweicaosheji#163.com与我联系处理。

相关文章

过年回家又是被讨论的对象
评论区被攻陷了
致ygz.ink的三周年
WordPress文章归档页面代码实现
祝大家元旦节快乐
从零开始:在自己服务器搭建Live2D看板娘&宠物猫咪(保姆级教程)

评论(2)

  1. Microsoft Edge 143 Microsoft Edge 143 Android 10 Android 10

    看到了,今天还在大佬论坛点进来,原来是你发的啊,哈哈

发布评论