缘由
其实这个项目算是一时兴起而来的,某一天突然想搞一个像 今日热榜 这样的网站,于是这个项目就诞生了。
最初时,由于对于 Node.js 了解得不够多,就草草选择了 Koa 框架作为了文件路由,并且没有采用 TypeScript,随着更多的小伙伴的参与开发后,当初随手写的路由框架就显得杂乱不堪了,并且也造成了一些维护上的问题,这时候重构就得提上日程了,虽然不是必需,但是我还是抽空花了几周时间将整个项目重写了一下。
重构
koa -> Hono
了解到这个 Hono 框架还是从 DIYgod 大佬写的一篇文章:一个六岁开源项目的崩溃与新生 中了解到的,看了官网和文档后,我也觉得这个框架不错,并且它还支持在多个平台上运行( 后面发现没这么简单 ),于是就着手替换。
这无疑是一个大工程,Hono 的中间件和 API 于 Koa 完全不同,需要重写原项目的所有路由和中间件,这算得上是一个大工程了。
JavaScript -> TypeScript
引入 TypeScript 主要是为了给项目增加类型安全性和更好的开发体验。在单人开发的项目中,使用 JavaScript 或者 TypeScript 其实没并没有什么区别,无非是少了一些编辑器的提示罢了,甚至可以说是多此一举,但是一旦项目开源,有其他开发者参与进来后,由于每个人的写法都可能不一样,这样就会导致后期难以维护,于是使用了 TypeScript 来保持代码的一致,和保持后续贡献的路由代码质量不至于太糟糕。
CommonJS -> ESM
ESM 是原生 JavaScript 模块系统,在现代浏览器中可以直接运行,同时 Node.js 也对此有支持。本项目当然也要跟上时代,于是全面更改为 ESM 模式( 主要是 ESM 支持了动态导入,可以实现路由文件的按需加载 )
HTML -> JSX
没办法,Hono 对原生的 HTML 支持性不太好( 也可能是我还不太会用 Hono ),只能使用 JSX 来实现一些静态页面,比如项目的启动首页和报错页面等。
新的功能
RSS
新版本支持了 RSS 模式,就是可以在各大阅读器中直接阅读热榜数据了。只需在请求的网址后面加上参数 rss=true 即可,例如:
GET https://www.example.com/bilibili?rss=trueLimit
由于部分榜单返回的数据量过多,特地添加了一个 limit 限制,你可以在请求的网址后面加上参数 limit=20 即可,例如:
GET https://www.example.com/bilibili?limit=20这样如果榜单的数据大于20条,就只会返回前 20 条数据。
获取最新数据
WARNING
注意,请勿频繁使用该功能调用接口,否则你将可能会遇到被封禁 IP 等其他问题
由于保证项目的稳定,采用了 60 分钟的缓存,在首次获取到原数据后,会将原数据缓存起来,在后续请求时,若未超出缓存时长,将直接采用缓存数据。但是如果你需要数据的时效性,你可以在请求的网址后面加上参数 cache=false 即可,这样每次请求时,都将返回最新数据,例如:
GET https://www.example.com/bilibili?cache=false新的路由
由于原本的路由文件过于繁杂,非常不便于后续的开发和维护,于是在本次重构时,对路由文件做出了重大修改:
- 现在所有路由文件都位于项目根目录下的
src/routes/目录中,网址路径以文件名为准。 - 基本所有的路由都采用如下规则,后续贡献路由将以下方代码为准:
// 最终导出的方法
// 接受传入参数并返回出最终列表数据
export const handleRoute = async (c: ListContext, noCache: boolean) => {
// 接受传入参数,网址 ? 后面的参数
const type = c.req.query("type") || "0";
// 获取返回的结果
const { fromCache, data, updateTime } = await getList({ type }, noCache);
// 生成最终数据
const routeData: RouterData = {
name: "bilibili",
title: "哔哩哔哩",
type: "热门榜",
description: "你所热爱的,就是你的生活",
parameData: {},
link: "https://www.bilibili.com/v/popular/rank/all",
total: data?.length || 0,
updateTime,
fromCache,
data,
};
return routeData;
};
// 这里是获取数据的方法
// 可能采用调用接口或者网页爬虫的方式
const getList = async (options: Options, noCache: boolean) => {
// 根据传入的参数来构建出请求的网址
const { type } = options;
const url = `https://api.bilibili.com/x/web-interface/ranking/v2?rid=${type}`;
// 发送请求获取地址数据
const result = await get({
url,
headers: {
Referer: `https://www.bilibili.com/ranking/all`,
},
noCache,
});
// 解析出列表原数据
const list = result.data.data.list;
return {
// 是否来自缓存
fromCache: result.fromCache,
// 数据更新时间
updateTime: result.updateTime,
// 最终的列表数据
data: list.map((v: RouterType["bilibili"]) => ({
id: v.bvid, // 唯一性 ID
title: v.title, // 文章标题
desc: v.desc, // 文章介绍
cover: v.pic.replace(/http:/, "https:"), // 文章封面
author: v.owner.name, // 文章作者
hot: v.stat.view, // 热门数据
url: v.short_link_v2 || `https://www.bilibili.com/video/${v.bvid}`, // 文章地址
mobileUrl: `https://m.bilibili.com/video/${v.bvid}`, // 文章移动端地址
})),
};
};关于部署
在一切都重构完成后,出现了一点小插曲,原来的 Vercel 部署方法不再适用了,所以目前对于 Vercel 部署支持暂时下线,待日后搞定了之后再说,如果目前仅能通过 Vercel 进行部署的小伙伴请不要更新到新版本 v2.0.0,请谅解。
当然,其他部署方式一切正常,在此还是推荐使用 Docker 方式进行部署,这种方式不易发生问题,并且过程十分简单,全程只需两行命令。
Docker 部署
安装及配置 Docker 将不在此处说明,请自行解决
本地构建
# 构建
docker build -t dailyhot-api .
# 运行
docker run -p 6688:6688 -d dailyhot-api
# 或使用 Docker Compose
docker-compose up -d在线部署
# 拉取
docker pull imsyy/dailyhot-api:latest
# 运行
docker run -p 6688:6688 -d imsyy/dailyhot-api:latest手动部署
最直接的方式,可以按照以下步骤部署在任何地方
安装
git clone https://github.com/imsyy/DailyHotApi.git
cd DailyHotApi然后再执行安装依赖
npm install开发
npm run dev成功启动后程序会在控制台输出可访问的地址
编译运行
npm run build
npm run start成功启动后程序会在控制台输出可访问的地址
Railway 部署
本项目支持使用 Railway 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。
Zeabur 部署
本项目支持使用 Zeabur 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。

