10 月,开发者不可错过的开源大数据大会-2021 WeDataSphere 社区大会深圳站 了解详情
写点什么

前端文档站点搭建方案

2021 年 3 月 20 日

前端文档站点搭建方案

前言


《自动化 Web 性能优化分析方案》一文中说到,百策系统性能检测的原理,以及对于检测页面我们最终会生成一份检测报告,如下图所示:



通过检测报告,我们可以清楚地了解到我们的页面在性能方面有哪些不足和有待提高的地方,并且针对每一个扣分项,我们都提供了详细的扣分原因,以及解决方案。


我们的系统是面向前端团队所有的开发同学,因此我们需要将我们的解决方案沉淀下来,群策群力,共同丰富优化性能的知识库,所以我们需要文档站点,一个可以实时编辑,自动部署的文档站点。


目前现有的文档站点方案有 docsify (https://docsify.js.org/#/?id=docsify)gitbook (http://gitbook.hushuang.me/)vuepress (https://vuepress.vuejs.org/zh/guide/) 等,也有着相对丰富的插件和主题,但是布局较为单一,灵活性不足。


需求


  • 文档用 Markdown 编写,最终生成 Html

  • 文档可以实时编辑,而不是修改 Html 代码

  • Markdown 文件修改后,文档站点自动更新


方法一:EggJS + marked + highlight.js


  • 将文档以 Markdown 的形式放在 GitLab 中,以便文档的维护

  • 熟悉 Git 的小伙伴知道 GitLab 是可以设置钩子 (Hook) 的,通过设置钩子可以实现当我们提交代码后,服务端知道在哪个分支修改了哪些文件,然后把更新后的文档重新转化成 Html 文件

  • 在设置 GitLab Webhook 时,只需选择 push event 就好,这样 Hook 就只会在 push 的时候触发,一个完整的 push event 返回的数据字段很多,对于我们来说,下面两个字段就足够了


{  ref: ''    // 分支名  commits: [   // 提交内容    {      added: [],  // 新增的文件路径      modified: [],  // 修改的文件路径      removed: []     // 删除的文件路径    }  ],  ...}
复制代码

整个流程大致如下:


模板文件即除了 Markdown 外的文件,譬如布局、样式、脚本等,这些公用文件有变动后需要将整个站点重新构建一遍。


在这个流程中,服务端需要开发两个接口,一个用来响应 GitLab 的 Webhook,另一个是用来进行手动批量生成。


// document.router.tsimport { Application } from "egg";export default (app: Application): void => {  const { controller, router } = app;  // gitlab webhook  router.post("/api/hook", controller.document.hook);  router.get("/api/batchUpdate", controller.document.batchUpdate);};
复制代码

Webhook


在设置 Webhook 的时候,我们只需选择 push event,这样就会在 push 的时候,才会触发 Hook,为了保证文档的规范,只对 Master 分支的 push event 进行操作。


Webhooks 配置:


代码示例:

const { commits, ref }: { commits: any[]; ref: string } = pushEvent;// 过滤非 master 分支的 pushif (!isMaster(ref)) {  return false;}// 修改(新增)文档列表const updateList: string[] = [];// 删除文档列表const removeList: string[] = [];commits.forEach((item: Commits) => {  const { added, modified, removed } = item;  updateList.push(...added, ...modified);  removeList.push(...removed);});// 过滤重复文件const uniqueUpdateList: string[] = [...new Set(updateList)];const uniqueRemoveList: string[] = [...new Set(removeList)];
复制代码


在得到要更新的集合 uniqueUpdateList 后,就要将对应的 Markdown 文件内容转为 Html,如何获取到 Git 上的单个文件呢,我们可以从 uniqueUpdateList 得知新增或修改的文件路径,然后我们需要借助 GitLab Open Api 中的 ${gitLabhost}/api/v3/projects/${projectId}/repository/files?file_path=${filePath}&ref=master&private_token=${accessToken} 获取对应文件的内容,完整的返回如下:


{  file_name: "",    // 文件名称  file_path: "",    // 文件路径  size: 700,     // 文件大小  encoding: "base64",     // 编码方式  content: "",     // 文件内容  ref: "master",   // 分支名 ...}
复制代码


通过这个接口可拿到 base64 编码的文件内容,转换成 uft-8 后就是我们需要的 Markdown 文档了。


new Buffer(content, "base64").toString("utf8");
复制代码


接下来需要将 Markdown 转换成 Html,Markdown 转 Html 使用的是 marked ,代码高亮使用的是 highlight.js 


import * as marked from "marked";import * as hljs from "highlight.js";marked.setOptions({  // 设置高亮  highlight(code, lang) {    if (lang && hljs.getLanguage(lang)) {      return hljs.highlight(lang, code, true).value;    } else {      return hljs.highlightAuto(code).value;    }  }});const customRender = new marked.Renderer();const htmlStr: string = marked(markdownStr, { renderer: customRender });
复制代码


全量生成


为了保证使用最新的模板和文档生成 html,每次全量生成之前,都需要从 GitLab 拉取完整的项目,拉取代码使用的是 git-clone (https://yarnpkg.com/zh-Hant/package/git-clone)


gitClone(repo, targetPath, {}, () => {  ...});
复制代码


接下来就是读取临时文件夹中 Markdown 文件内容,结合页面模板转化为 Html。


页面模板即除了文档内容 Markdown 外的文件,譬如布局、公用头部、左侧菜单、样式、脚本等。


然后将前面生成的正文内容注入到准备好的模板中,这里使用的模板引擎是 Ejs,就可以得到如下页面:



方法二:NestJS + docsify


本着折腾的精神,上述方法是我们组的小伙伴自己实现了 docsify 类似的方案,细节方面的设计着实比不上开源的那些产品,所以我们决定再折腾一次,迁移到了方案二:node.js + docsify。服务端负责处理 Webhooks 来拉取 GitLab 上最新的文档文件,docsify 负责实时编译文档。


docsify 是一个动态生成文档网站的工具。不同于 GitBook、Hexo 的是,它不会在服务端编译时将 md 文件转成 Html 文件,所有转换工作都是在浏览器端执行的。docsify 已经提供了实施编译 md 文件的功能,剩下我们需要实现的部分就是在 GitLab 上的文件有更新时,自动触发服务重新拉取最新的 md 文件。


改造后的流程:

  • 文档贡献者在 GitLab 上编辑源文件

  • 编辑完成保存后触发 GitLab 的 Webhooks

  • 文档服务接收到 Webhooks 请求后拉取最新的文档

  • 用户刷新页面后 docsify 实时把最新的 md 文件转化为 Html


服务端核心代码

// app.controller.tsimport { Controller, Post } from "@nestjs/common";import * as execa from "execa";@Controller()export class AppController {  @Post("hook")  async hook() {    // 执行命令 git pull,拉取最新代码    const { stdout } = await execa("git", ["pull"]);    return stdout;  }}
复制代码


// main.tsimport { NestFactory } from "@nestjs/core";import { AppModule } from "./app.module";import { join } from "path";import { Logger } from "@nestjs/common";const port = parseInt(process.env.PORT, 10) || 3001;async function bootstrap() {  const app = await NestFactory.create(AppModule);  app.setGlobalPrefix("api");  app.useStaticAssets(join(__dirname, "..", "docs"));  await app.listen(port);  Logger.log(`服务已启动,请访问 http://localhost:${port}`);}bootstrap();
复制代码


效果图

点击编辑文档即可进入文档对应的 GitLab 页面进行编辑。


方案对比


方案二和方案一不同的地方就是把 md 文件渲染成 Html 的一步从服务端改到了浏览器端,服务器端只承担接收 Webhook 拉取最新的代码的工作。


总结


以上提供了一个不用 GitLab CI 实现文档站点内容修改后自动更新的思路。一个顺手的文档站点搭好之后,接下来我们就只需要关心如何把 Markdown 写好推送到 GitLab,其它的工作服务器都会帮我们完成。


头图:Unsplash

作者:牧羊

原文:https://mp.weixin.qq.com/s/youcldtW12xEr3T4OkXnRw

原文:前端文档站点搭建方案

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021 年 3 月 20 日 00:122135

评论

发布
暂无评论
发现更多内容

「深度解析」AI训练之数据缓存

焱融科技

人工智能 AI 存储 焱融科技 数据缓存

一致性hash的理解与实现

dongge

很多人毕业多年以后,还是改不掉学生思维

小智

职场 思维方式 高考

干货 | 如何评估Kubernetes持久化存储方案

焱融科技

Kubernetes 容器 云原生 k8s 容器存储

计算机操作系统基础(十四)---线程同步之条件变量

书旅

php laravel 操作系统 进程 线程’

第 5 周作业:一致性 Hash 算法

姜 某某

计算机中短期学习路线

zack

架构师训练营第五周课后总结

Cloud.

数据分析师成长体系漫谈--数据埋点

analysis-lion

数据分析 数据采集 埋点

Ceph数据恢复初探

焱融科技

焱融科技 文件存储 分布式存储 数据恢复 Ceph

架构师训练营第5周

大丁💸💵💴💶🚀🐟

用进废退,增加能力熟练度与经验值,让你的技能再次精进。

叶小鍵

这份高考卷,只有程序员能得满分...

程序员生活志

程序员 高考

女同事问哪吒什么是 Spring 循环依赖?我...

通天哪吒

Redis-进阶篇一

多选参数

数据库 redis redis高可用 redis6.0.0 Redis项目

一致性 hash 算法

Z冰红茶

一致性Hash算法

一次非常有意思的 SQL 优化经历: 从 30248.271s 到 0.001s

Java小咖秀

MySQL 面试 经验分享 优化逻辑 后端开发

啃碎并发(三):Java线程上下文切换

猿灯塔

话题讨论|作为一名程序员,你下班之后都会做些什么?

InfoQ写作平台官方

写作平台 话题讨论 话题

让Go“恐慌”的十种方法

博文视点Broadview

Go

【Python】__name__ 是什么?

Leetao

Python Python基础

week05 学习总结 分布式缓存&消息队列&负载

Z冰红茶

第五周作业

Linuxer

极客大学架构师训练营

啃碎并发(二):Java线程的生命周期

猿灯塔

超详细!一文带你了解 LVS 负载均衡集群!

JackTian

Linux 负载均衡 运维 LVS 服务器集群

架构师训练营第五周作业

一剑

Spring核心原理解析

Chank

Java spring

第5周结构师训练营——作业

jiangnanage

游戏夜读 | 关卡设计的难点

game1night

SpringBoot 中使用 Filter 的正确姿势

Java课代表

一口气说出 OAuth2.0 的四种授权方式

程序员内点事

Java oauth2.0

前端文档站点搭建方案-InfoQ