前言
之前看过国外一个开发者 tipsy 用 kotlin 开发了一个 github profile summary, 我觉得他的想法非常棒, 所以我想着用 Node 的技术栈来实现一遍
我的技术栈是 vue 和 Koa, 所以在实现上我就用了它们分别来做前端和后台
先放张效果图:
项目结构
- .
- LICENSE
- README.md
- app.js
- build
- build.js
- check-versions.js
- logo.png
- utils.js
- vue-loader.conf.js
- webpack.base.conf.js
- webpack.dev.conf.js
- webpack.prod.conf.js
- config
- dev.env.js
- index.js
- prod.env.js
- index.html
- jsconfig.json
- now.json
- package.json
- server
- controllers
- middlewares
- router
- utils
- src
- App.vue
- assets
- components
- main.js
- router
- views
- static
- yarn.lock
基本就是 vue-cli 生成的项目结构 + Koa 后端的项目结构, 这个就不再赘述
思路
利用 github 的 api 来获取所需要的用户信息, 然后通过获取的用户信息来构建用户的 github 简历一开始我打算用的也是 github 的 RESTful api 不过马上我就遇到问题:
github 的 RESTful api 是有分页限制的也就是比如我无法一次性获取一个用户的点过 star 的仓库, 而需要判断是否有下一页是否要请求到最后
RESTful 的 api 会返回一堆我不需要的数据而且数据量一大对性能要求会比较高比如我只想要一个用户的仓库总数, 我却需要获取完他所有的仓库我才能知道总数
要发多个 RESTful 请求才能勉强完成一些功能, 代价是耗时巨大
综上, 无法再继续用 RESTful api 来实现我想要的效果于是我把目光转移到了 GraphQL api 上了相信很多关注前端的朋友们对于这个词并不陌生, 但是真正用上的少之又少毕竟目前还是 RESTful api 为主的开发模式
我也一样, 一开始看 GraphQL api 的那些定义结构什么的, 感到非常困难不过借助 Github 的 GraphQL exploer 以及 Github GraphQL 的官方开发者论坛, 总算是大致实现了我想要的功能
github 提供的 GraphQL api 有一些特别有用的属性比如 totalCount, 就能计算一些参数的总数, 比如上面说到的一个人拥有的仓库总数, 而不用一个个仓库都获取才知道总数
目前来说, GraphQL 还不像 RESTful 那样通过直观的 url 来查询数据, 而是通过用户自定义的 graphql 语句来实现需要的信息的查询 比如统计用户 Molunerfinn 的仓库总数:
- {
- user(login: "Molunerfinn") {
- repositories(affiliations: [OWNER COLLABORATOR] isFork: false) {
- totalCount
- }
- }
- }
返回结果:(跟你的 graphql 结构是一致的)
- {
- "data": {
- "user": {
- "repositories": {
- "totalCount": 24
- }
- }
- }
- }
所以现在 node 社区还没有什么特别好的库来像数据库的 ORM 一样帮我们简化写 graphql 的步骤所有的请求都必须自己手写 graphql
项目流程
由于 github 的 api 查询数量限制, 在有 token 的情况下一小时是 5000 次, 所以 tipsy 的 github profile summary 做了限制, 查询某个用户的 github profile summary 时必须先 star 这个仓库, 才可以查询因此我也做了一个这个的限制
所以我在后端 Koa 处做了检测的接口, 用于检测用户是否 star, 既可以防止恶意刷请求又可以给自己涨一波 star 岂不美哉~
前端 Vue 页面这边发起请求后判断返回值, 如果是 false 说明并没有 star, 所以就不让其跳转的具体的 profile 页面
- async checkStar () {
- this.loading = true
- let res = await this.$http.get(`/api/check-status/${this.username}`)
- if (res.data.success) {
- this.$router.push({
- name: 'Profile',
- params: {
- username: this.username
- }
- })
- localStorage.setItem('github-profile-token', res.data.token)
- } else {
- this.invalid = true
- this.invalidUsername = this.username
- this.loading = false
- }
- }
如果请求结果正确, 会把后端返回的 json web token 存入 localStorage 里用于鉴权
当然, 如果用户直接跳转 profile 页面, 也会在路由的钩子里判断 token 存不存在? 如果存在, 放行如果不存在, 请求一遍用户是否 star, 如果 star, 放行, 否则不放行
其实业务逻辑都很简单, 相信做过 node 前后端的你们能很快理解为了减轻服务器的负担, 后端在通过 GraphQL 请求完数据后, 直接返回前端, 利用前端浏览器的算力, 来把数据组织出来通过 chart.js 来渲染出好看的图表:
来源: https://juejin.im/post/5a7bfcb06fb9a0634f408666