这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战。
前言
玩转掘金 系列,对掘金扩展一些有意思的功能,比如:
某天进入个人主页一看,自己已经给别人点赞过千
了,我滴个神。
作为新人,互相支持一下,也算正常,我的这些💗,都给了谁呢,追寻真像,自己动手实现一个吧。
效果演示
详情列表是可以点击进入文章页面的
源码地址
源码地址:JJMyStarAndC
后端服务采用nodejs编写,你需要安装对应的安装包。
实现思路
可行的思路:
- Chrome插件 + 游猴脚本
- 静态页面 + nginx代理
- 静态页面 + 服务转发
- ......
本文的思路是 静态页面
+ nodejs自定义服务
,
纯属为了好玩额外用了 Server Sent Events , 简称SSE,服务器端单边推送,其作用是服务端获取数据后,单边推动给客服端。
有人可能会说,咋不用 socket.io
, 我这里额外说一下, socket.io
号称socket中的jQuery, 这个意思,大家懂了吧,而且还要配套其服务端库一起使用,讨厌!!
更好的实现方案:透传即可
express
+ http-proxy-middleware
实现细节
数据获取
一切均围绕数据展开,没有数据,别画饼,别谈理想。
对于Star获取,一个接口足以!
请求地址: api.juejin.cn/interact_ap…
请求方式: post
请求参数:
其有没有隐藏的limit
参数选项呢? 大家可以尝试一波!
{
cursor: `${cursor}`, // 起始查询位置
item_type: 2, // 文章点赞,对应有沸点点赞
sort_type: 2, // 排序,有近导员
user_id: uid, // 用户id
}
返回结果:
{
has_more: true,
data: [{
author_user_info:{
user_id: "3465271329953806", // 用户ID
user_name: "小魔童哪吒" // 用户名
},
article_info: {
article_id: "6996484371305725965" // 文章ID
title: "k8s 学习二" // 文章标题
}
}]
}
请求的时候, cursor
参数非常重要,表示指针位置,从哪继续往后走。
返回结果里面,很重要的字段has_more
, 表示还没有数据,如果有,继续获取。
掘金很多的接口,起初没做限制的,当初我记得某些接口可以传入limit
参数为1000,也可以,后来做了改进,值得赞一波。
其循环获取核心代码:
while (res.has_more) {
data.cursor = `${cursor}`
res = (await axios.default.post(url, data, {
headers
})).data;
cursor += 10;
await delay(undefined, 16).run(); // 暂停16ms, 故意的
}
我们说了,这里是服务端获取了数据,还没推动到前端。
数据推送
nodejs端使用SSE,也很简单,我这里就没有使用三方库了。
- request传了uid,rid两个参数 uid表示用户ID, rid表示requestId
getStars
表示开始查询- SSE的核心就在于
Content-Type
与Connection
'Content-Type': 'text/event-stream'
申明类型
'Connection': 'keep-alive'
表示不关闭连接 - 因为这里也属于一个请求,所以我们借助事件中心来派发事件给请求,请求再回写数据
app.get('/sseStream', function (request, response) {
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
const { uid, rid } = request.query;
console.log("uid:", uid);
// 查询用户的点赞
getStars(uid, rid);
// 产线可别这么用
eventsCenter.removeAllListeners("ssePush");
// 事件中心
eventsCenter.on("ssePush", function (event, data) {
// console.log("push message to clients");
response.write("event: " + String(event) + "\n" + "data: " + JSON.stringify(data) + "\n\n");
});
});
那事件是哪派发出来的,就是数据获取那里派发的,我补上代码, 这里多了两种外的事件messageTotal
, messageEnd
, 一个是总数消息,有个是表示请求结束的消息。
async function getStars(uid, rid) {
let cursor = 0
const data = {
cursor: `${cursor}`,
item_type: 2,
sort_type: 2,
user_id: uid,
}
let res = {
has_more: true
};
while (res.has_more) {
data.cursor = `${cursor}`
res = (await axios.default.post(url, data, {
headers
})).data;
console.log("res:", data, res)
eventsCenter.emit("ssePush", "messageTotal", {
uid,
rid,
count: res.count
});
eventsCenter.emit("ssePush", "message", {
uid,
rid,
datas: (res.data || []).map(d => ({
user_id: d.author_user_info.user_id,
user_name: d.author_user_info.user_name,
title: d.article_info.title
}))
});
cursor += 10;
await delay(undefined, 16).run();
}
eventsCenter.emit("ssePush", "messageEnd", {
uid,
rid
})
}
前端数据获取
前台对应监听事件就好了, 就这么简单。
const source = new EventSource(`/sseStream?uid=${uid}&rid=${rid}`);
// 收到新数据
source.addEventListener('message', function (e) {
let data = JSON.parse(e.data)
// 不是需要的数据
if (data.uid != uid || data.rid != rid) {
return;
}
listArr.push(...data.datas);
// console.log("listArr:", listArr);
renderList(listArr);
gotStarsEl.innerHTML = listArr.length;
}, false)
// 收到总数据消息
source.addEventListener('messageTotal', function (e) {
let data = JSON.parse(e.data)
// 不是需要的数据
if (data.uid != uid || data.rid != rid) {
return;
}
totalStarsEl.innerHTML = data.count;
}, false)
// 统计完毕
source.addEventListener('messageEnd', function (e) {
let data = JSON.parse(e.data)
console.log("meesage", data);
}, false)
统计和分组
- 统计: 以用户ID为key,没有则新建,有则修改基数。
- 对keys进行map转数组,然后sort。
就这么简单!
const statObj = list.reduce((obj, cur) => {
if (hasOwnProperty.call(obj, cur.user_id)) {
obj[cur.user_id].count += 1;
obj[cur.user_id].items.push(cur);
} else {
obj[cur.user_id] = {
items: [cur],
count: 1,
...cur
};
}
return obj;
}, {});
// 分组
const groupList = Object
.keys(statObj)
.map(k => statObj[k]) //分组
.sort((a, b) => a.count > b.count ? -1 : 1); // 排序
更多的实现细节,请移步源码。
写在最后
3-5分钟,500-1000字,有所得,而不为所累,如果你觉得不错,你的一赞一评就是我前行的最大动力。
技术交流群请到 这里来。 或者添加我的微信 dirge-cloud,一起学习。