这是一份写给开发者、设计师、气象爱好者的共建指南。
也是一篇碎碎念,关于我为什么要做这个东西。
一、缘起
超强台风"巴威"逼近的那个凌晨,我睡不着。
不是因为怕,是白天当了一天牛马,脑子转不动了,躺床上习惯性打开手机,想看看台风到底什么情况。结果越刷越乱:
- 这个博主说"史上最强,千万别出门",
- 那个博主说"没那么严重,都是标题党",
- 划两下刷到一条三天前的旧闻, 还有人在评论区吵"到底信谁的"。
刷了二十分钟,我一个消息没搞清楚, 倒是学会了三种"台风穿搭"和两首"台风BGM"。 躺在出租屋的床上,我脑子里其实只剩三个问题
- 台风现在在哪、多强?
- 离我家还有多久?
- 我现在到底该干嘛?
就这三个问题,翻遍手机里的软件, 居然没有一个地方能一次讲清楚。
索性爬起来,打开电脑,用 Claude Fable 5 把想法一点点敲了出来。把基于中央气象台、日本气象厅、美国 JTWC、台湾气象署四家的路径数据放在同一张地图上之后,很多事一眼就看明白了——比如此刻,几家的预测登陆点正在往温州附近收敛。
然后顺手加了几样东西:
- 实时新闻聚合。比官方数据更快,分布式数据的二次验证,但不一定准,请自行甄别;
- 城市波及倒计时。大概还有多久,巴威到你所在的城市,基于风速和地图向量估算的,不一定准;
- 应急指南和紧急电话。真到了要用的时候,不用再去搜。
这个网站纯粹是自己做的:没有广告,不收集数据,数据来源全是官方,防灾决策请以官方预警为准(不信谣、不传谣)。
做这个的时候就一个念头:能让身边人早知道一点,心里也能踏实一点。
如果你有江浙沪的朋友,欢迎转给 ta。如果你是开发者,往下读——我会把它怎么运转的、哪里还很粗糙、你能怎么帮忙,都摊开讲清楚。
二、它长什么样
先看图,再聊技术。
打开就是一张会动的地图
实测路径按国标六级强度配色,从深海一路"烧"到近海。左边挂着一列城市倒计时,最紧迫的排最前面。顶上一条预警横幅,告诉你现在谁最危险。
点一下你关心的城市
镜头飞过去,弹出一张小卡片:风圈预计什么时候到、最近距离多少公里、现在该做什么。分享链接也帮你想好了——?city=温州,发给温州的朋友,打开直接聚焦到 ta 家。
四家机构的预报,摆在一起看
中、日、美、台四家的预报虚线可以独立开关。预报本来就有分歧,分歧本身就是信息——四条线收得越拢,说明路径越确定;散得越开,越要多留个心眼。
新闻和指南,都在手边
新闻是媒体视角,比官方报文快,但请自行甄别。指南是静态内容,从"来临前 48 小时"写到"过后安全确认",紧急电话都列好了——真正慌乱的时刻,没人有耐心再去搜索。
三、它是怎么转起来的
架构简单到有点不好意思:一个 Cloudflare Worker,一堆纯函数,没有数据库,没有服务器。
┌─────────────────────────────────────────────────────────────┐
│ 用户浏览器(MapLibre GL + TypeScript) │
│ · 路径回放 / 风圈动画 / 城市标记 / HUD / 分享深链 │
└──────────────────────────┬──────────────────────────────────┘
│ /api/typhoon/:id /api/news
┌──────────────────────────▼──────────────────────────────────┐
│ Cloudflare Worker(边缘代理 + 缓存 + 容灾) │
│ · normalize.ts 双源归一化 │
│ · news.ts RSS 资讯聚合 │
│ · 缓存:台风 5min / 资讯 10min │
└──────────┬─────────────────────────────┬────────────────────┘
│ │
浙江水利厅 API(主) Google/Bing News RSS
中央气象台 NMC(备)
但简单是刻意的。灾害面前,系统越简单越可靠。 没有数据库可以宕机,没有服务器需要半夜爬起来重启,边缘节点挂了还有下一个。一个防灾工具首先要做到的,是它自己别先倒下。
选型时想的也是同一件事:
| 层级 | 选型 | 为什么 |
|---|---|---|
| 边缘计算 | Cloudflare Workers | 零运维、全球 CDN、国内可访问 |
| 前端地图 | MapLibre GL | 开源、灵活,不依赖 Google |
| 底图 | 高德卫星 + 中文注记 | 国内 CDN 稳,中文标注,合规踏实 |
| 构建 | Vite + TypeScript | 快,且类型兜底 |
| 数据 | 无数据库 | 源站返回全量轨迹,边缘缓存就够了 |
目录一览
worker/
index.ts # 路由、边缘缓存、双源容灾
normalize.ts # CMA/ZJ 数据 → TyphoonData(纯函数)
news.ts # RSS 解析、聚合与快照合并(纯函数)
src/
app.ts # 入口:回放控制、HUD、分享、倒计时刷新
map.ts # MapLibre 图层、城市标记、风圈动画
impact.ts # 城市波及倒计时算法(纯函数)
geo.ts # 球面距离、风圈多边形、轨迹插值
guide.ts # 应对指南内容
news.ts # 资讯面板渲染
mobile.ts # 移动端布局:顶部实况条 + 底部三档抽屉
slogan.ts # 正能量口号轮播
几个写代码时守着的习惯:
- 数据归一化和算法尽量写成纯函数(
normalize.ts、impact.ts、geo.ts),好测、好复用,出了问题也好定位; - 画图和动画都关在
map.ts里,业务逻辑不跟渲染纠缠; - Worker 只做代理和缓存。它越"笨",越不容易坏。
一个踩过的坑,分享给你
新闻聚合上过一次当。原来的逻辑是每次抓取后覆盖缓存快照——某个时刻 Google 被限流、Bing 恰好只回了 1 条,这份贫瘠的结果就把之前积累的 30 条完整快照冲掉了。用户打开一看,资讯页只剩孤零零一条。
修复之后,刷新改成了合并:新条目优先,历史条目去重补位,七天窗口内只增不减。这个教训值得写在这里:对一个防灾工具来说,一次糟糕的抓取,永远不该毁掉已有的积累。
四、如何上手?
git clone git@github.com:Trade-Offf/typhoon-bavi-tracker.git
cd typhoon-bavi-tracker
npm install
npm run dev # Vite 开发服务器
npm run typecheck # TypeScript 检查
npm run preview # 构建 + wrangler 本地模拟 Worker
本地 vite dev 时,/api/typhoon 会代理到浙江水利厅接口。想看完整的 Worker 行为(缓存、容灾、新闻聚合),用 npm run preview。
五、这些事,等你来做
下面的方向按"能多帮到一个人"排序,不按技术难度。在这个项目里,加一个城市比换一个框架更有价值。
防灾价值直接提升
| 方向 | 说明 | 从哪改 |
|---|---|---|
| 增加城市 | 在 CITIES 数组加 { name, lng, lat },就这么简单 | src/impact.ts |
| 10/12 级风圈倒计时 | 扩展 computeImpacts 返回多级 ETA | src/impact.ts |
| 按定位排序城市 | navigator.geolocation + 距离排序 | src/app.ts |
| 多台风切换 | TYPHOON_ID 改成路由参数——台风年年来,这个网站不该只为巴威活一次 | src/app.ts · worker/index.ts |
| 地方化指南 | 各省防汛细则、避险点。你比我更懂你的城市 | src/guide.ts |
体验与传播
| 方向 | 说明 |
|---|---|
| i18n 繁体/英文 | 面向港澳台与外籍居民 |
| 预警等级接入 | 对接中央气象台预警信号 |
| PWA 离线缓存 | 断网前的最后一次数据,离线也能看——台风天最容易断网 |
| 分享卡片动态生成 | OG 图按城市倒计时实时渲染 |
数据源扩展(需合规评估)
| 源 | 现状 | 思路 |
|---|---|---|
| 小红书 | 反爬,无法匿名抓取 | 需用户授权或官方 API |
| 微博 | 需访客 Cookie | Worker 端谨慎实现,注意 ToS |
| 地方气象局 | 多数有公开接口 | 在 worker/ 加 fetcher 模块 |
性能待办(欢迎认领)
- 风圈多边形点数按需简化(低缩放级别)
- Service Worker 缓存最近一次 API 响应
- 截图/OG 图 WebP 压缩
- 单元测试:
impact.ts·normalize.ts·geo.ts
六、提 MR 的姿势
流程很平常,Fork、开分支、自测、发 PR:
git checkout -b feat/your-feature-name
npm run typecheck # 必须过
npm run build # 必须过
npm run preview # 本地把 Worker + 前端跑通
几个小约定:
- commit message 讲"为什么",别只写"做了什么";
- 一个 MR 只做一件事。加城市、修数据源、改样式,分开提;
- 附上验证方式。截图或录屏,让 review 的人两分钟能确认。
比如这样:
feat(impact): 新增厦门、泉州城市倒计时
- 在 CITIES 数组补充闽南沿海城市
- 验证:打开 /?city=厦门 可看到倒计时弹窗
Review 时我会看类型安全、依赖体积、数据源的超时与容灾。但有一条比所有代码规范都重要:
这个项目的每一行文案,都可能在某个深夜被一个正在担心家人的人读到。 我们提供确定性,不制造焦虑;传递紧迫感,不贩卖恐慌。免责声明必须保留:倒计时是估算,防灾决策以官方预警为准。
七、为什么开源
防灾信息不应该被锁在任何人的服务器里——包括我的。
这个项目诞生于一个睡不着的凌晨,但它不该只属于那个凌晨。台风年年来,数据源会失效,城市名单会过时,指南会需要更新。一个人的热情撑不起一件需要长期存在的事,能撑起它的只有社区。
开源之后,事情就变得可以想象:数据源挂了,有人半小时内提个 fix;福建的朋友补上福建的避险点;有人 fork 一份部署到自己的域名,哪怕有一天 chinaupdated.com 不在了,代码还在,还能救急。
写代码的人常常怀疑自己做的东西有没有意义。但防灾这件事给了一个罕见的、清晰的答案——
技术人的理想主义,有时候就是写一段代码,让多一个人提前看到危险。
你修的每一个 bug,可能就是某个用户在断网前抢到的最后一次刷新;你加的每一个城市,背后都是真实住在那里的人。这里没有 KPI,没有增长曲线,只有一个朴素的度量:有没有人因为它,提前把窗户加固了、把水囤上了、把老人接走了。
欢迎 Star、Fork、提 Issue、提 MR。一个人力量有限,众人拾柴,总能多照亮一段路。
台风会走,代码会留下。愿它下一次被用到时,比这一次更好。
附录
自定义域名在 wrangler.jsonc 的 routes 中配置,当前绑定 chinaupdated.com 与 www.chinaupdated.com。
截图索引
| 文件 | 说明 |
|---|---|
docs/screenshots/01-overview-city-countdown.png | 总览:高德底图 + 城市列表 + 预警条 |
docs/screenshots/02-city-popup.png | 点击城市:倒计时弹窗 + 行动建议 |
docs/screenshots/03-news-panel.png | 实时资讯:多源媒体报道 |
docs/screenshots/04-guide-panel.png | 应对指南:五段式清单 |
docs/screenshots/05-forecast-compare.png | 四机构预报路径对比 |