2025/12/24 每日总结 天气预报应用 - APISpace
一、准备工作
在开始编码前,我们需要先准备好核心依赖和资源:
1. 技术栈与资源
- 基础技术:原生 HTML/CSS/JavaScript(无框架,聚焦核心逻辑)
- UI 增强:Font Awesome 图标库(提供天气相关图标)
- API 服务:APISpace 天气预报 API(支持获取实时天气、未来预报等数据)
2. API 前期配置(关键步骤)
天气预报应用的核心是真实的天气数据,因此第一步要完成 API 的申请和配置:
- 注册 APISpace 账号,进入「天气系列 API」模块,选择合适的天气预报接口(比如“全国天气预报查询”);
- 申请接口的使用权限,获取专属的
API Key(接口调用的身份凭证); - 查看接口文档,确认请求方式(POST/GET)、请求参数(如城市名、城市编码)、返回数据格式(JSON)。
提示:APISpace 提供免费调用额度,足够开发和测试使用;接口文档会明确标注请求头、参数说明,这是后续对接的关键。
二、核心实现步骤
1. 页面 UI 搭建:打造高颜值界面
先完成页面的结构和样式,核心目标是美观、响应式、符合用户体验:
(1)页面结构设计
整体分为 5 个核心模块:
- 头部:应用标题和说明
- 搜索框:输入城市名查询天气
- 加载/错误提示:交互反馈
- 天气卡片:展示实时天气、天气指数(体感温度、湿度、风速等)
- 未来预报栏:展示多日天气预报
(2)关键样式技巧
- 渐变背景+毛玻璃效果:通过
linear-gradient设置渐变背景,结合backdrop-filter: blur()实现卡片的毛玻璃质感,提升视觉层次; - 响应式布局:使用 CSS Grid 和 Flex 布局,配合媒体查询适配移动端;
- 交互动效:卡片 hover 上移、按钮 hover 变色,增强交互体验。
2. API 对接:从模拟到真实数据
代码中最初用 setTimeout 模拟 API 调用,实际开发中需要替换为真实的 API 请求,这是核心环节:
(1)API 调用逻辑(替换模拟代码)
// 替换原有的模拟 getWeatherData 函数
function getWeatherData(city) {
loader.style.display = 'block';
errorMessage.style.display = 'none';
// 真实 API 请求配置
const apiUrl = 'https://eolink.o.apispace.com/weather/v001/weather'; // 接口地址(以APISpace为例)
const headers = {
'X-APISpace-Token': '你的API Key', // 替换为自己的API Key
'Content-Type': 'application/x-www-form-urlencoded'
};
const params = new URLSearchParams({
city: city, // 要查询的城市名
days: '7' // 获取7天预报
});
// 发起 API 请求
fetch(apiUrl, {
method: 'POST',
headers: headers,
body: params
})
.then(response => {
if (!response.ok) throw new Error('请求失败');
return response.json();
})
.then(data => {
// 解析API返回的真实数据(需根据接口文档调整字段)
const weatherData = {
location: data.city,
temperature: data.now.temp,
condition: data.now.weather,
feelsLike: data.now.feels_like,
humidity: data.now.humidity,
windSpeed: data.now.wind_speed,
pressure: data.now.pressure,
uvIndex: data.now.uv_index,
visibility: data.now.visibility,
airQuality: data.now.air_quality,
clothing: getClothingAdvice(data.now.temp) // 自定义穿衣建议逻辑
};
updateWeatherUI(weatherData);
// 更新未来预报数据
updateForecast(data.forecast);
loader.style.display = 'none';
})
.catch(error => {
loader.style.display = 'none';
errorMessage.style.display = 'block';
errorMessage.querySelector('p').textContent = `获取数据失败:${error.message}`;
console.error('API请求错误:', error);
});
}
// 新增:更新未来预报
function updateForecast(forecastData) {
const forecastItems = document.querySelectorAll('.forecast-item');
forecastData.forEach((item, index) => {
if (index < forecastItems.length) {
forecastItems[index].querySelector('.forecast-date').textContent = item.date;
forecastItems[index].querySelector('.forecast-temp').textContent = `${item.temp_high}°C`;
forecastItems[index].querySelector('.forecast-condition').textContent = item.weather;
// 更新预报图标
forecastItems[index].querySelector('i').className = getWeatherIcon(item.weather);
}
});
}
(2)关键注意事项
- 跨域问题:直接在前端调用第三方 API 可能出现跨域,解决方案有两种:
- 后端代理:通过自己的后端服务转发 API 请求,前端调用后端接口;
- 配置 CORS:确认 API 提供商是否支持跨域,或在开发环境使用跨域插件。
- 数据解析:不同 API 的返回字段命名不同,需严格按照接口文档解析,避免字段错误导致渲染失败;
- 异常处理:添加请求失败的提示,提升用户体验。
3. 交互逻辑实现
完成 API 对接后,补充交互逻辑,让应用更易用:
- 搜索功能:点击搜索按钮/按下回车,触发天气查询;
- 加载状态:请求数据时显示加载动画,避免用户误以为无响应;
- 天气图标适配:根据返回的天气状况(晴天/多云/小雨等),自动切换对应的 Font Awesome 图标;
- 响应式适配:确保在手机、平板等设备上正常显示。
三、完整代码
以下是整合后的完整代码(包含模拟数据,替换 API 部分即可使用真实数据):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气预报应用 - APISpace</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2980, #26d0ce);
color: #fff;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
padding: 20px 0;
margin-bottom: 30px;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
header p {
opacity: 0.9;
font-size: 1.1rem;
}
.search-box {
display: flex;
justify-content: center;
margin-bottom: 30px;
}
.search-box input {
padding: 15px 20px;
width: 100%;
max-width: 400px;
border: none;
border-radius: 30px 0 0 30px;
font-size: 1rem;
outline: none;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.search-box button {
padding: 15px 25px;
border: none;
border-radius: 0 30px 30px 0;
background: #ff6b6b;
color: white;
cursor: pointer;
font-size: 1rem;
font-weight: bold;
transition: background 0.3s;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.search-box button:hover {
background: #ff5252;
}
.weather-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 25px;
margin-bottom: 30px;
}
.weather-card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 25px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
transition: transform 0.3s;
}
.weather-card:hover {
transform: translateY(-5px);
}
.weather-card h2 {
margin-bottom: 15px;
font-size: 1.5rem;
display: flex;
align-items: center;
gap: 10px;
}
.current-weather {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.temperature {
font-size: 3.5rem;
font-weight: bold;
margin: 15px 0;
}
.weather-details {
display: flex;
flex-direction: column;
gap: 10px;
}
.detail {
display: flex;
justify-content: space-between;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
padding: 8px 0;
}
.forecast {
display: flex;
overflow-x: auto;
gap: 15px;
padding: 10px 5px;
margin-bottom: 30px;
}
.forecast-item {
flex: 0 0 auto;
width: 150px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 15px;
text-align: center;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.forecast-date {
font-weight: bold;
margin-bottom: 10px;
}
.forecast-temp {
font-size: 1.5rem;
margin: 10px 0;
}
.api-info {
text-align: center;
background: rgba(0, 0, 0, 0.2);
padding: 15px;
border-radius: 15px;
margin-top: 20px;
}
.loader {
display: none;
text-align: center;
margin: 30px 0;
}
.loader .spinner {
border: 5px solid rgba(255, 255, 255, 0.3);
border-top: 5px solid #fff;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
text-align: center;
background: rgba(255, 0, 0, 0.2);
padding: 15px;
border-radius: 10px;
margin: 20px 0;
display: none;
}
@media (max-width: 768px) {
.weather-cards {
grid-template-columns: 1fr;
}
.current-weather {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><i class="fas fa-cloud-sun"></i> 天气预报</h1>
<p>使用APISpace API获取实时天气数据</p>
</header>
<div class="search-box">
<input type="text" id="city-input" placeholder="输入城市名称...">
<button id="search-btn"><i class="fas fa-search"></i> 搜索</button>
</div>
<div class="loader">
<div class="spinner"></div>
<p>正在获取天气数据...</p>
</div>
<div class="error" id="error-message">
<p><i class="fas fa-exclamation-circle"></i> 无法获取天气数据,请检查网络连接或稍后再试。</p>
</div>
<div class="weather-cards">
<div class="weather-card" id="current-weather">
<h2><i class="fas fa-location-dot"></i> 当前位置</h2>
<div class="current-weather">
<div>
<div class="temperature">25°C</div>
<div class="weather-condition">晴天</div>
</div>
<div class="weather-details">
<div class="detail">
<span>体感温度</span>
<span>26°C</span>
</div>
<div class="detail">
<span>湿度</span>
<span>65%</span>
</div>
<div class="detail">
<span>风速</span>
<span>3.6 km/h</span>
</div>
<div class="detail">
<span>气压</span>
<span>1015 hPa</span>
</div>
</div>
</div>
</div>
<div class="weather-card">
<h2><i class="fas fa-info-circle"></i> 天气指数</h2>
<div class="weather-details">
<div class="detail">
<span>紫外线</span>
<span>中等</span>
</div>
<div class="detail">
<span>能见度</span>
<span>10 km</span>
</div>
<div class="detail">
<span>空气质量</span>
<span>良好</span>
</div>
<div class="detail">
<span>穿衣建议</span>
<span>短袖衣物</span>
</div>
</div>
</div>
</div>
<h2 style="margin: 25px 0 15px;"><i class="fas fa-calendar-days"></i> 未来天气预报</h2>
<div class="forecast">
<div class="forecast-item">
<div class="forecast-date">明天</div>
<i class="fas fa-cloud" style="font-size: 2rem;"></i>
<div class="forecast-temp">26°C</div>
<div class="forecast-condition">多云</div>
</div>
<div class="forecast-item">
<div class="forecast-date">周二</div>
<i class="fas fa-cloud-rain" style="font-size: 2rem;"></i>
<div class="forecast-temp">23°C</div>
<div class="forecast-condition">小雨</div>
</div>
<div class="forecast-item">
<div class="forecast-date">周三</div>
<i class="fas fa-cloud-sun" style="font-size: 2rem;"></i>
<div class="forecast-temp">25°C</div>
<div class="forecast-condition">多云转晴</div>
</div>
<div class="forecast-item">
<div class="forecast-date">周四</div>
<i class="fas fa-sun" style="font-size: 2rem;"></i>
<div class="forecast-temp">28°C</div>
<div class="forecast-condition">晴天</div>
</div>
<div class="forecast-item">
<div class="forecast-date">周五</div>
<i class="fas fa-sun" style="font-size: 2rem;"></i>
<div class="forecast-temp">29°C</div>
<div class="forecast-condition">晴天</div>
</div>
</div>
<div class="api-info">
<p>数据由 <strong>APISpace</strong> 提供 | Moonbeams----张一衡</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchBtn = document.getElementById('search-btn');
const cityInput = document.getElementById('city-input');
const loader = document.querySelector('.loader');
const errorMessage = document.getElementById('error-message');
// 默认显示北京天气
getWeatherData('北京');
searchBtn.addEventListener('click', function() {
const city = cityInput.value.trim();
if (city) {
getWeatherData(city);
}
});
cityInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
const city = cityInput.value.trim();
if (city) {
getWeatherData(city);
}
}
});
function getWeatherData(city) {
// 显示加载动画
loader.style.display = 'block';
errorMessage.style.display = 'none';
// 模拟API调用,实际应用中替换为真实API请求
setTimeout(() => {
// 模拟数据
const weatherData = {
location: city,
temperature: Math.round(Math.random() * 15 + 20), // 20-35°C
condition: ['晴天', '多云', '小雨', '阴天'][Math.floor(Math.random() * 4)],
feelsLike: Math.round(Math.random() * 15 + 22),
humidity: Math.round(Math.random() * 40 + 40), // 40-80%
windSpeed: (Math.random() * 10 + 2).toFixed(1),
pressure: Math.round(Math.random() * 20 + 1000), // 1000-1020
uvIndex: ['低', '中等', '高'][Math.floor(Math.random() * 3)],
visibility: Math.round(Math.random() * 10 + 5), // 5-15km
airQuality: ['优', '良好', '中等'][Math.floor(Math.random() * 3)],
clothing: ['短袖衣物', '长袖衣物', '薄外套', '厚外套'][Math.floor(Math.random() * 4)]
};
// 更新UI
updateWeatherUI(weatherData);
// 隐藏加载动画
loader.style.display = 'none';
}, 1500);
}
function updateWeatherUI(data) {
// 更新当前天气
document.querySelector('#current-weather h2').innerHTML = `<i class="fas fa-location-dot"></i> ${data.location}`;
document.querySelector('.temperature').textContent = `${data.temperature}°C`;
document.querySelector('.weather-condition').textContent = data.condition;
// 更新天气详情
document.querySelectorAll('.detail')[0].lastElementChild.textContent = `${data.feelsLike}°C`;
document.querySelectorAll('.detail')[1].lastElementChild.textContent = `${data.humidity}%`;
document.querySelectorAll('.detail')[2].lastElementChild.textContent = `${data.windSpeed} km/h`;
document.querySelectorAll('.detail')[3].lastElementChild.textContent = `${data.pressure} hPa`;
// 更新天气指数
document.querySelectorAll('.weather-details .detail')[4].lastElementChild.textContent = data.uvIndex;
document.querySelectorAll('.weather-details .detail')[5].lastElementChild.textContent = `${data.visibility} km`;
document.querySelectorAll('.weather-details .detail')[6].lastElementChild.textContent = data.airQuality;
document.querySelectorAll('.weather-details .detail')[7].lastElementChild.textContent = data.clothing;
// 根据天气条件更新图标
const conditionIcon = document.querySelector('#current-weather h2 i');
conditionIcon.className = getWeatherIcon(data.condition);
// 更新预报(示例)
const forecastItems = document.querySelectorAll('.forecast-item');
forecastItems.forEach(item => {
const temp = Math.round(Math.random() * 10 + 20); // 20-30°C
item.querySelector('.forecast-temp').textContent = `${temp}°C`;
});
}
function getWeatherIcon(condition) {
switch(condition) {
case '晴天':
return 'fas fa-sun';
case '多云':
return 'fas fa-cloud';
case '小雨':
return 'fas fa-cloud-rain';
case '阴天':
return 'fas fa-cloud';
default:
return 'fas fa-cloud';
}
}
});
</script>
</body>
</html>
四、遇到的问题与解决
- 跨域问题:直接调用 APISpace API 出现跨域,解决方案是在本地开发环境使用
vite/webpack配置代理,转发 API 请求; - 数据兼容性:不同城市的返回数据可能存在空值,需添加默认值处理(如
data.now.temp || '未知'); - 图标适配:天气状况的描述不统一(如“晴”和“晴天”),需统一判断逻辑。
五、总结
关键点回顾
- API 配置核心:获取 API Key 后,需严格按照接口文档配置请求头和参数,处理返回的 JSON 数据;
- 前端交互优化:添加加载/错误提示、响应式布局、动效,提升用户体验;
- 实战技巧:模拟数据先行,再替换真实 API,降低调试难度;处理边界情况(空数据、请求失败),保证应用稳定性。

浙公网安备 33010602011771号