最近在维护博客侧边栏时,遇到了一次非常典型、也非常“真实”的问题:

最近刚换上的 Hexo 时钟插件,突然因为第三方 API 挂了,直接失效。

这篇文章记录了我从问题出现到完整解决的全过程,包括插件改造与 npm 发布。


这篇文章会讲什么

  • 排查 hexo-butterfly-clock-remake 失效原因

  • 自行修改并重写 IP 定位逻辑

  • 接入 高德地图 Web 服务 API

  • 打包并发布 自己的 npm 插件

  • 完成 Hexo 插件的安装与更新


一、问题背景:IP 定位接口失效

我上一篇 Blog 使用的是下面这个插件:

hexo-butterfly-clock-remake
用于 Butterfly 主题侧边栏显示:时间 + 天气 + 城市

原插件依赖的 IP 定位接口为:

https://api.vore.top/api/IPdata

某天开始出现异常,表现为:

  • 插件完全不渲染

  • 浏览器控制台请求失败

结论非常明确:

第三方 IP 定位 API 已不可用(接口直接炸了)

而插件本身已经多年未维护,也没有任何备用定位方案。


二、解决思路:用高德地图 Web 服务接管 IP 定位

问题集中在 IP → 城市 这一步,因此整体思路非常清晰:

  • 保留原有插件结构与注入方式

  • 只替换 IP 定位来源

  • 不影响原有 和风天气(QWeather) 逻辑

为什么选择高德地图?

高德地图 Web 服务提供了稳定可靠的 IP 定位接口:

https://restapi.amap.com/v3/ip?key={你的Key}

优点非常明显:

  • 接口稳定

  • 国内访问速度快

  • 返回城市 / 省份信息已足够使用

  • 免费额度对博客场景完全充足


三、我具体做了哪些改动

1. 新增配置项

在站点配置或主题配置中新增一项:

gaud_map_key: xxxxxxxxxxxxxxxxxxxxx

说明:

  • 这是 高德地图 Web 服务 Key

  • 与 JS SDK Key 不同

  • 专用于 IP 定位接口


2. 修改 IP 获取逻辑

原逻辑(简化)

IP API → city → QWeather

新逻辑

高德 IP API → city / province → QWeather

当 IP 接口返回的 city 为空时,自动 fallback 到 province,最大限度避免出现「未知城市」。


四、为什么我选择单独发布一个新插件

我没有直接向原仓库提 PR,而是选择:

  • 独立维护

  • 保持向下兼容

  • 重新发布 npm 包

  • 不影响原插件用户

最终插件命名为:

hexo-butterfly-clock-veeink

📦 GitHub 仓库地址:
👉 https://github.com/kitia01/hexo-butterfly-clock-veeink

这是一个 只增强、不破坏 的改进版本。


五、发布 npm 包的全过程

1. 登录 npm

npm login

按提示输入:

  • npm 用户名

  • 密码

  • 邮箱

  • OTP(如开启 2FA)


2. 配置 package.json

确保关键信息正确:

{ "name": "hexo-butterfly-clock-veeink", "version": "1.0.0", "main": "index.js", "keywords": ["hexo", "butterfly", "clock"], "license": "MIT" }


3. 发布到 npm(重点)

npm publish

⚠️ 注意:npm 不允许覆盖已发布版本号,每次发布必须升级版本。


六、Hexo 中如何安装 / 更新插件

1️⃣ 安装

(1)卸载旧时钟插件(如存在)

1
2
3
4
npm uninstall hexo-butterfly-clock
npm uninstall hexo-butterfly-clock-anzhiyu
npm uninstall hexo-butterfly-clock-anzhiyu-yang
npm uninstall hexo-butterfly-clock-remake

(2)安装本插件

npm install hexo-butterfly-clock-veeink


2️⃣ 使用方式

(1)添加配置

在站点配置 _config.yml 或主题配置 _config.butterfly.yml 中加入:

使用前需注册 和风天气开发者账号
👉 https://dev.qweather.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# electric_clock
# see:https://github.com/kitia01/hexo-butterfly-clock-veeink
electric_clock:
enable: true # 插件开关
priority: 5 # 过滤器优先级
enable_page: all # 应用页面,可写特定路径或 "all"
exclude: # 排除页面,可留空或写具体路径
# - /posts/
# - /about/
layout:
type: class # 容器类型,class 或 id
name: aside-content # 目标容器的 class 或 id
# insert_before: user-countdown # 插入到该元素前面
insert_after: card-announcement # 插入到该元素后面(二选一)
loading: https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.0/lib/loading.gif
clock_css: https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.0/lib/clock-min.css
clock_js: https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.0/lib/clock-min.js
qweather_api_host: {YOUR API HOST}
qweather_key: {YOUR KEY}
# 新增:高德地图 Web 服务 Key
gaud_map_key: {YOUR KEY} # 高得地图web服务key
default_city: "资兴"

(2)参数说明

参数 类型 释义
priority number 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enable true/false 【必选】控制开关
enable_page path 【可选】填写想要应用的页面,如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填all,默认为all
exclude path 【可选】填写想要屏蔽的页面,可以多个。写法见示例。原理是将屏蔽项的内容逐个放到当前路径去匹配,若当前路径包含任一屏蔽项,则不会挂载。
layout.type id/class 【可选】挂载容器类型,填写id或class,不填则默认为id
layout.name id/class 【必选】挂载容器名称
layout.insert_before id/class 【可选】 插入到该元素前面(二选一)
layout.insert_after id/class 【可选】插入到该元素后面(二选一)
loading URL 【可选】电子钟加载动画的图片
clock_css URL 【可选】电子钟样式CDN资源
clock_js URL 【可选】电子钟执行脚本CDN资源
qweather_key key 【必选】和风天气 key
qweather_api_host URL 【必选】和风天气 api_host
gaud_map_key string 【可选】高得地图web服务key,如果没有填写则根据默认城市优先选择
default_city string 【可选】当默认城市为空,优先根据IP定位,填写了默认城市将优先使用默认城市的定位和天气

3️⃣ 重新生成博客

hexo cl hexo g hexo s

确认侧边栏:

  • 时间

  • 天气

  • 城市定位

均正常显示。


七、踩坑记录(真实)

  • ❌ JS 压缩(minify)时错误混淆作用域,运行时报 ReferenceError

  • ❌ 过度依赖第三方 API 的稳定性

  • ✅ Key 通过配置传入,而非写死

  • ✅ IP 定位失败时增加 fallback,避免 UI 直接炸掉


八、总结

这次经历让我非常清楚地意识到:

博客长期稳定 ≠ 第三方 API 永久可靠

同时也完整走了一遍 npm 插件发布流程

整体并不复杂,但每一步都有坑。

如果你也在使用 hexo-butterfly-clock-remake,并遇到城市定位失效的问题,希望这个插件和这篇记录能帮到你。