用nextjs给掘金的读者发制作徽章墙(0.1版)--4/1000

我是天元,立志做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

如果你喜欢的话,请点赞,收藏,转发

评论领取永不言败勋章

历史文章

相关推荐
理想不理想v2 分钟前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫2 分钟前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.8 分钟前
Chrome调试工具(查看CSS属性)
前端·chrome
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
阿伟来咯~2 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store