前言

我的项目正式版公布了,最近比拟安闲,晃荡的时候看到rsshub这个库,想玩一下,然而rsshub文档里的接口太多了,本人手敲又嫌麻烦,突发奇想解析rsshub的文档主动生成所有文档呈现的api给前端页面应用。

不是什么起因我的yarn和npm都装不了rsshub这个包,我是间接拉GitHub的rsshub下来用的,能装的能够间接装置,不行的也能够像我一样拉下来用,就是有点俊俏。

我的项目地址

rsshub-demo

效果图

没有特地去写款式,有局部接口有问题,可能获取不到数据

代码

首先第一步也是最外围的一步就是解析文档,提取无效信息,当然是用正则去解析对应的api名称,题目,path等,我定义了一个常量先申明这些正则规定。

const RegMap = {  Type: new RegExp(/\s#\s(.+)\s/g),  Content: new RegExp(    /\s##.+\s+###.+\s+(<Route.+>)|(<\/Route>\s+###.+\s+<Route.+>)/g  ),  Title: new RegExp(/\s##\s(.+)\s/g),  ChildTitle: new RegExp(/\s###(.+)/g),  Route: new RegExp(/(\<Route.+\>)/g),  Example: new RegExp(/example="(\S+)"/g),  Path: new RegExp(/path="(\S+)"/g),  ParamsDesc: new RegExp(/paramsDesc="(.+)"/g),};

解析文档内容,这里我是间接下载了文档的,你也能够去申请文档来解析,找无效信息的代码略微有点low,解析之后生成接口具体的题目、path等信息的json文件

let type;  const data = await fs    .readFileSync(join(__dirname, `../../docs/${file}`))    .toString();  data.replace(RegMap.Type, (_, val) => (type = val));  const list = data.match(RegMap.Content);  if (!list) return;  const result = [];  for (let index = 0; index < list.length; index++) {    const content = list[index];    let title, childTitle, route, example, path, paramsDesc;    content.replace(RegMap.Title, (_, val) => (title = val));    content.replace(RegMap.ChildTitle, (_, val) => (childTitle = val));    content.replace(RegMap.Route, (_, val) => (route = val));    if (route) {      route.replace(RegMap.Example, (_, val) => (example = val));      route.replace(RegMap.Path, (_, val) => (path = val));      route.replace(RegMap.ParamsDesc, (_, val) => (paramsDesc = val));    }    if (title) {      result.push({        title,        children: [          {            title: childTitle,            example,            path,            paramsDesc,          },        ],      });    } else {      const parent = getParent(result, index);      if (parent) {        parent.children.push({          title: childTitle,          example,          path,          paramsDesc,        });      }    }  }  fs.writeFileSync(`./api/${file}.json`, JSON.stringify({ type, result }));

读取生成的接口信息json文件。

 const apiPath = join(__dirname, "../api");  const files = await fs.readdirSync(apiPath);  files.forEach(async (file) => {    const data = await fs.readFileSync(join(apiPath, file)).toString();    routeList.push(JSON.parse(data));  });

应用express创立服务,读取并解决接口json,配置对应的后端api,并向前端页面提供routeList,不便一会页面间接获取其余接口。

const express = require("express");const RSSHub = require("./RSSHub/lib/pkg");const app = express();const createRoute = require("./utils/create-route");function GetData(url) {  return new Promise((res, rej) => {    RSSHub.init({});    RSSHub.request(`https://rsshub.app${url}`).then(res).catch(rej);  });}app.use(express.static("./static"));const list = [];await createRoute(list);const routeList = list    .map((v) => v.result)    .flat()    .map((v) => v.children)    .flat();    // 依据routeList配置apirouteList.forEach((route) => {    app.get(`/api${route.example}`, async function (req, res) {      const data = await GetData(route.example);      res.send(data.item);    });});// 给前端提供routeList,不便前端调用其余接口app.get("/api/alldata", (req, res) => {    res.send(list);});app.listen(8088, () => {    console.log("http://localhost:8088 端口启动".green);});

页面上应用axios发动申请,应用petite-vue疾速铺数据

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script><script type="module">    import { createApp } from 'https://unpkg.com/petite-vue?module'    import { API } from './api.js'    createApp({      navList: [],      subNavList: [],      data: [],      selete: {        nav: '',        subNav: '',        title: '',        menu: '',      },      titleList: [],      menus: [],      content: '',      link: '',      async mounted() {        const navList = await axios.get('/api/alldata')        this.navList = navList      },      onClickNav(item) {        this.selete.nav = item.type        this.subNavList = item.result      },      onClickSubNav(item) {        this.selete.subNav = item.title        if (item.children.length) {          this.titleList = item.children          this.selete.title = item.children[0].title          axios.get(`/api${item.children[0].example}`).then(data => {            this.menus = data          })        }      },      onClickTitle(item) {        this.selete.title = item.title        axios.get(`/api${item.example}`).then(data => {          this.menus = data        })      },      onMenuItem(menu) {        this.selete.menu = menu.title        this.content = menu.description        this.link = menu.link      }    }).mount()  </script>    <div id="app" v-scope @vue:mounted="mounted">    <div class="top">      <div class="nav">        <div class="nav-item" :class="{selected:selete.nav===item.type}" v-for="item in navList" :key="item.type"          @click="onClickNav(item)">          {{item.type}}        </div>      </div>      <div class="sub-nav" v-show="subNavList.length">        <div class="sub-nav-item" :class="{selected:selete.subNav===item.title}" @click="onClickSubNav(item)"          v-for="item in subNavList" :key="item.title">          {{item.title}}        </div>      </div>    </div>    <div class="bot" v-show="menus.length">      <div class="title">        <div class="title-item" :class="{selected:selete.title===item.title}" v-for="item in titleList"          @click="onClickTitle(item)">{{item.title}}</div>      </div>      <div class="menu">        <div class="menu-item" :class="{selected:selete.menu===menu.title}" v-for="menu in menus" :key="menu.link"          @click="onMenuItem(menu)">          {{menu.title}}        </div>      </div>      <div class="content" v-if="content" v-html="content"></div>      <iframe v-else :src="link" frameborder="0" width="100%" height="2000px"></iframe>    </div>