暂未播放
0:00
0:00

给博客侧边栏加一个天气组件

782 字
4 分钟
给博客侧边栏加一个天气组件

侧边栏一直空着一块位置,想着放个天气小组件挺实用——一眼看到温度、空气质量,点开能看湿度和风力。做完发现坑不少:Astro 的 scoped 样式对运行时注入的 DOM 不生效、localStorage 缓存解包把数据搞丢、Swup 页面切换脚本不重入。

组件基于 Firefly 主题开发,主要用到它的侧边栏 widget 注册体系。

核心思路#

博客是纯静态站,天气数据只能在浏览器里 fetch,没法构建时拿。这决定了两件事:接口得允许跨域,国内那些要登录、要 Key、还常限频的基本可以排除。

最后定了四个源,原则是能免 Key 就免 Key、国内优先:

数据源Key角色
uapis.cn不需要国内主力,单接口返回全部,反射 Origin 放行跨域
和风天气需要兜底,实时更准
Open-Meteo不需要坐标出中国包围盒就用,免费不限频
wttr.in不需要全球兜底,不带空气质量

整个组件最关键的设计是四源归一化:不管哪个源,全部 map 成同一套 {cur, daily, aqi, city},渲染层完全不关心数据从哪来。后面换源、加源都不用动 UI。配置中心(weatherConfig)+ fetcher(拉 + 归一化 + 缓存)+ 渲染三层分离,widget 只管画。

uapis

归一化

和风

Open-Meteo

wttr

统一结构 {cur,daily,aqi,city}

渲染层

uapis

归一化

和风

Open-Meteo

wttr

统一结构 {cur,daily,aqi,city}

渲染层

几个真正卡人的点#

1. scoped 样式对动态 DOM 不生效

组件运行时往 DOM 塞内容,Astro 的 scoped <style> 只对编译期存在的节点打 hash,运行时注入的吃不到。表现就是样式明明写对了,刷新出来没效果。解法要么用 :global(),要么把样式丢进 <style is:global> 或干脆 inline。这个坑不在逻辑,而在「为什么样式不生效」这个排查方向上。

2. Swup 重入

Firefly 用 Swup 做 SPA 跳转,脚本不重载但 DOM 会换。所有客户端脚本要 document.addEventListener("astro:page-load", fn) 重新绑定,首屏再直接 fn() 跑一次(防错过首次事件),并用 dataset.bound 之类的标志位防重复绑。不这么干,翻几页事件就绑了好几遍,打字机动画还会两段打架——用递增 token 作废上一段动画:

let _token = 0;
function loop() {
const my = ++_token;
// 循环里每步先判断 if (my !== _token) return;
}
document.addEventListener("astro:page-load", loop);
loop();

3. 缓存别裸存数组

localStorage 里直接 setItem(JSON.stringify(arr)) 看着省事,结构一变老访客解包就丢数据或报错。我存 {ts, data},带 6 小时 TTL,结构变了老缓存自动失效重拉:

const ok = Array.isArray(obj?.data) && Date.now() - (obj?.ts || 0) < TTL;

单位与映射的坑#

  • 和风风速是 m/s,要 ×3.6 成 km/h;Open-Meteo 用 wind_speed_unit=kmh 直接是 km/h;wttr 的 windspeedKmph 已是 km/h。别混。
  • 风向 16 方位压成 8 方位:Math.round(((deg + 22.5) % 360) / 45) % 8
  • beaufort 等级用速度中点表近似,展示够用,别追求精确。
  • 空气质量:uapis 直接给 aqi 值,和风走 /airquality,Open-Meteo 没有 AQI 得另算,兜底层级要想清楚。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
给博客侧边栏加一个天气组件
https://x1anyu.cn/posts/3/
作者
羡鱼
发布于
2026-08-05
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
羡鱼
临渊空慕水中鱼, 不如携风自渡河.
分类
标签
最新动态
站点统计
文章
14
分类
7
标签
35
总字数
19,969
运行时长
0
最后活动
0 天前
文章目录