前言
我的项目正式版公布了,最近比拟安闲,晃荡的时候看到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>