OpenWeatherMap API 浏览器扩展:实用指南
I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source link you’ve already provided) here? Once I have the article’s body, I’ll translate it into Simplified Chinese while preserving the original formatting, markdown, and any code blocks or URLs.
介绍
如果你正在构建一个显示天气数据的浏览器扩展,OpenWeatherMap 是首选。它们的免费层真的很有用,API 文档完善,并且非常适合按需调用 API 的扩展(而不是从服务器调用)。
免费层限制
- 每分钟 60 次调用
- 每月 1,000,000 次调用
- 访问 Current Weather Data API
- 访问 5 Day / 3 Hour Forecast API
对于一个新标签页扩展,即使有 10 000 名活跃用户每天打开 20 个标签页,也需要约 ~200 000 次调用/天(约 ~6 M/月)。如果使用 10 分钟缓存,则会降至约 ~600 000/月——轻松低于免费额度。
每位用户的 API 密钥
每个用户都需要拥有自己的 API 密钥。你不能在公开分发的扩展中捆绑单个密钥,因为该密钥会从源代码中被提取,导致任何人都能耗尽你的配额。
存储用户的密钥
// Extension popup/settings page
async function saveApiKey(key) {
await chrome.storage.local.set({ owmApiKey: key });
}
使用用户的密钥获取天气
// In newtab.js
async function getWeather(city) {
const { owmApiKey } = await chrome.storage.local.get('owmApiKey');
if (!owmApiKey) {
// Show API key setup prompt
showApiKeyPrompt();
return;
}
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${owmApiKey}&units=metric`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 401) {
showApiKeyError('Invalid API key');
}
return;
}
return response.json();
}
好处
- 保持用户数据私密(他们的密钥、他们的账户)
- 分发时无需后端
- 防止 API 密钥从扩展中被提取
- 将配额责任明确归于每位用户
缓存以减少调用
在每次打开标签页时调用 API 是浪费的,并且会很快触及速率限制。
const CACHE_DURATION = 10 * 60 * 1000; // 10 minutes
async function getWeatherWithCache(city) {
const cacheKey = `weather_${city}`;
const cached = JSON.parse(localStorage.getItem(cacheKey) || 'null');
if (cached && (Date.now() - cached.timestamp) {
const date = new Date(item.dt * 1000);
const dayKey = date.toDateString();
const hour = date.getHours();
// Prefer readings around noon (12‑15h)
if (!byDay[dayKey] || (hour >= 12 && hour day !== today)
.slice(0, 3)
.map(([day, item]) => ({
date: day,
temp_min: Math.round(item.main.temp_min),
temp_max: Math.round(item.main.temp_max),
icon: item.weather[0].icon,
description: item.weather[0].main
}));
}
天气图标
OpenWeatherMap 的图标地址为:
https://openweathermap.org/img/wn/{icon}@2x.png
如果你更喜欢自定义图标,可以将天气代码映射到表情符号或自己的 SVG:
function weatherCodeToEmoji(iconCode) {
const code = iconCode.substring(0, 2); // '01', '02', etc.
const isDay = iconCode.endsWith('d');
const map = {
'01': isDay ? '☀️' : '🌙',
'02': isDay ? '⛅' : '☁️',
'03': '☁️',
'04': '☁️',
'09': '🌧️',
'10': isDay ? '🌦️' : '🌧️',
'11': '⛈️',
'13': '❄️',
'50': '🌫️'
};
return map[code] || '🌡️';
}
地理位置 vs. 手动输入
自动检测位置是理想的,但需要用户授权,且可能会失败。提供手动输入城市的备用方案。
async function initWeather() {
// Try geolocation first
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
async (pos) => {
const { latitude, longitude } = pos.coords;
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}`;
// ... fetch and display
},
() => {
// Geolocation denied or failed
showCityInputForm();
},
{ timeout: 5000, maximumAge: 60 * 60 * 1000 } // Cache for 1 hour
);
} else {
showCityInputForm();
}
}
生产检查清单
- API:OpenWeatherMap 免费套餐,用户提供的密钥
- 缓存:10 分钟
localStorage缓存 - 单位:根据位置自动检测公制/英制,用户可覆盖
- 回退:如果地理位置被拒绝,手动输入城市
- 错误处理:
401– 错误的 API 密钥404– 未找到城市- 网络错误 – 显示通用重试信息
该扩展是开源的;您可以查看实现以获取更多细节。