我是天元,立志做
1000个有趣的项目
的前端,公众号:cssandjs
如果你喜欢的话,请
点赞,收藏,转发
。
评论
领取创始者
勋章
背景
最近掘金的每篇文章,我都在顶部写了领取xx徽章
,其实这就是我早就想好的,我要制作一个徽章墙给所有评论的人。
目前因为自己的域名备案还没通过,所以只能烦请各位大老爷自己运行下代码
现在已经迭代到0.1版本
,目前效果如下。
github地址:github.com/tinlee/1000...
需求分析
- 目前域名备案还没下来,所以目前一切从简。
- 第一个版本,以快速为主,所以直接使用文件做存储,第二个版本再做数据库
- 需要一个获取掘金评论数据的接口
- 需要一个展示勋章的页面
- 因为同时需要接口和页面,所以采用nextjs作为框架,同时保证了前后端的需求
徽章的生成
推荐使用scenario.com来生成,是专门做游戏素材的ai生成网站。
编码部分
安装
请按照nextjs.org上的方法进行项目相关的安装和初始化,中文版的略微滞后,有break change
bash
npx create-next-app@latest
这里的安装按照个人喜好就可以了。
bash
yarn add axios
yarn dev
安装axios
并启动项目
资源放置
在public中建立badge
文件夹,放置徽章相关资源。 同时新建一个database
文件夹,用于放置数据相关资源。因为涉及到具体用户的信息,这里放置后,请求比较方便。
新建articel.json
,放置我们的文章及徽章相关信息。
json
[
{
"url": "https://juejin.cn/post/7347910198784999461",
"name": "父爱如山",
"icon": "father.png"
},
{
"url": "https://juejin.cn/post/7346421986607087635",
"name": "零花钱+100",
"icon": "gold.png"
},
{
"url": "https://juejin.cn/post/7348327193061507108",
"name": "早发现早治疗",
"icon": "treat.png"
},
{
"url": "https://juejin.cn/post/7353106546828460047",
"name": "永不言败",
"icon": "phoenix.png"
}
]
这里是我的4篇文章,列举了徽章所需的文章地址,徽章名称以及图案信息。
新建请求接口
nextjs
既可以使用一个react组件作为页面显示,也可以使用接口模式,建立一个接口。
在src/app
下新建spider/route.js
js
import { NextResponse } from "next/server";
import axios, { AxiosResponse } from "axios";
import fs from "fs";
export async function GET(request, context) {
return NextResponse.json(
{ data: 'its test'},
{ status: 200 }
);
}
访问http://localhost:3000/spider
,这个页面就是我们获取掘金数据的接口了。
理解掘金数据获取规则
我们在掘金中点击查看全部评论的时候,会请求一个接口 这个接口就是获取评论的接口,它本身是分页加载的,因为我们是披露获取的,所以我们把cursor
改成0
,把limit
改成999
。
对route.js
进行补充
js
import { NextResponse } from "next/server";
import axios, { AxiosResponse } from "axios";
import articels from "../../../public/database/articel.json";
import fs from "fs";
function getPromise({ url }) {
const itemId = url.split("/").pop(); // 获取文章itemId
return axios.post("https://api.juejin.cn/interact_api/v1/comment/list", {
item_id: itemId,
item_type: 2,
cursor: "0",
limit: 999,
sort: 0,
client_type: 2608,
});
}
export async function GET(request, context) {
const promise = articels.map(getPromise); // 构建所有文章的请求
const res = await Promise.all(promise);
// 因为一个用户可以存在多个徽章,所以这里用一个map接收
const user = {};
// 遍历请求数据并将结果和徽章信息绑定
res.forEach((item, index) => {
const { icon, name } = articels[index];
item.data.data.forEach((subitem) => {
const { user_info, comment_info } = subitem;
const { ctime } = comment_info;
const { user_name, user_id, job_title, company, avatar_large } =
user_info;
if (!user[user_id]) {
user[user_id] = {
user_name,
ctime,
user_id,
job_title,
company,
avatar_large,
badges: [{ icon, name }],
};
} else {
user[user_id].badges.push({ icon, name });
}
});
});
// 在写入的时候,对用户进行排序
fs.writeFileSync(
"./public/database/user.json",
JSON.stringify(Object.values(user).sort((a, b) => b.ctime - a.ctime))
);
return NextResponse.json({ data: "获取成功" }, { status: 200 });
}
- 从
public/database/articel.json
获得文章数据 - 分割
url
获取itemId
,并构建请求 - 将所有请求数据进行提取,构建页面所需的数据
- 将数据按照
评论时间排序
并保存到database/user.json
中
至此相关的数据准备已经就绪了,我们最终形成了如下的数据结构
这里的badges
是一个数组,用于一个用户多次评论的场景
构建展示页面
修改 展示页面根据自己的喜好即可,唯一需要注意的是数据的请求地址
js
axios.get("/database/user.json")
这里的数据也可以使用fs
去读取,但是考虑之后我会将数据入库和分页,所以这里直接用了请求。相关代码如下: src/app/page.js
js
"use client";
import styles from "./page.module.css";
import axios from "axios";
import { useEffect, useState } from "react";
export default function Home() {
const [user, setUser] = useState([]);
useEffect(() => {
axios.get("/database/user.json").then((res) => {
setUser(Object.values(res.data));
});
}, []);
return (
<div className={styles.container}>
{user.map((item) => {
return (
<div key={item.user_id} className={styles.user}>
<div className={styles.header}>
<div className={styles.headerImage}>
<img src={item.avatar_large} />
</div>
<div className={styles.userName}>{item.user_name}</div>
<div className={styles.job}>
{[item.job_title, item.company]
.filter((item) => item)
.join("@")}
</div>
</div>
<div className={styles.badges}>
{item.badges.map((bg, ind) => {
return (
<div key={ind} className={styles.badgesItem}>
<img src={"/badge/" + bg.icon} />
<span className={styles.badgesName}>{bg.name}</span>
</div>
);
})}
</div>
</div>
);
})}
</div>
);
}
page.module.css
css
.container {
margin: 0 auto;
max-width: 680px;
padding: 20px;
font-size: 14px;
}
.user {
padding: 16px;
margin: 16px 0;
border-radius: 6px;
border-bottom: 1px solid #ccc;
background: #fff;
border: 1px solid rgb(208, 215, 222);
box-shadow: rgba(31, 35, 40, 0.04) 0px 1px 0px 0px;
}
.header {
display: flex;
align-items: center;
padding-bottom: 16px;
color: color rgb(31, 35, 40);
}
.headerImage {
width: 40px;
height: 40px;
border-radius: 20px;
overflow: hidden;
margin-right: 8px;
}
.headerImage img {
width: 100%;
height: 100%;
display: block;
}
.userName {
font-weight: 400;
line-height: 36px;
padding-right: 8px;
}
.job {
color: rgb(99, 108, 118);
}
.badges {
display: flex;
background-color: rgb(246, 248, 250);
padding: 16px;
}
.badgesItem {
margin-right: 20px;
}
.badgesItem img {
width: 60px;
height: 60px;
display: block;
}
.badgesName {
text-align: center;
color: rgb(31, 35, 40);
display: block;
padding-top: 8px;
}
最终效果
todolist
- 数据入库
- 文章新增接口,徽章管理
- 展示页面分页加载和搜索
- 服务器部署和cdn
我是天元,立志做
1000个有趣的项目
的前端,公众号:cssandjs
如果你喜欢的话,请
点赞,收藏,转发
。
评论
领取永不言败
勋章