先说结论: 语雀是目前编辑体验最好(没有之一)的文档平台,对开发者和非开发者都十分友好。强大的文本编辑功能,友好的使用体验, 更强大的markdown语法超集,流程图、思维导图, 多媒体资源上传插入(再也不用劳心劳力找图床了)。开放的API(本文的前提,这个貌似是唯一一家开放了文章内容API接口的文档平台)。语雀,很geek。

一、为什么还需要DIY

  • 语雀的缺点:
    • 个人版不支持绑定域名,且官方明确了不会支持。
    • 博客体验差, 还不能个性化的配置。想在样式上加个东西基本没戏。
    • 与语雀生态绑定在一起, 这点不爽。


  • 需求:
    • 编辑功能一定要好!编辑功能一定要好!编辑功能一定要好!
    • 自由:支持自定义域名,可定制程度高,博客上放什么完全我说了算。
    • 极简主义: 专注,简单,重点要突出文章本身而不是眼花缭乱的特效和广告。
  • 现有的博客系统都不怎么喜欢:
    • github pages 很好,但是太慢而且还是需要自己解决编辑工具问题, hexo等纯净态流同理
    • wordpress太臃肿。
  • 自动维护https证书,文章变更、新增无需build和deploy自动生效。
  • 基于以上需求, 语雀提供了其文章的API可以方便的获取知识库和文章数据,成功点燃了我蠢蠢欲动的小火苗。

二、 设计思路

  • 好的博客功能 = 好的编辑功能 + 方便的文章发布,利用语雀完美的编辑功能,通过开放接口实现博客的实时展示。
  • 回归专注: 专注于文章创作而非专注于博客本身。部署完成后只需要在语雀上写文章就行, 写好文章语雀发布也相当于在博客发布, 完全免运维。
  • 不能把所有文章都做展示,语雀区分知识库和文章两级角色。通过配置指定知识库来设置博客展示的文章范围, 博客仅会去取指定知识库下的文章内容并展示。
  • 无数据库,所有数据都从语雀获取, 仅仅依赖一个config.yaml进行基础设置。
  • 考虑到属于丢在那里会被遗忘(写文章和编辑都只需要在语雀即可,服务端一次配置部署后就可以忘掉了)所以需要具备自动维护Https证书的能力。
  • 博客的文章分类:利用语雀知识库, 每一个分类的文章写在一个单独的知识库里面。
  • 文档搜索: 利用语雀的搜索API实现。
  • 利用acme 的golang库实现自动https证书维护。
  • 博客的基本功能: title,副标题,keywords、description灯通过定义配置文件实现, 配置文件demo:
---
yuque:
  api: "https://www.yuque.com/api/v2"
  token: "token"
  user: "userxxxxx"
  repos:
    - name: "运维笔记"
      repo: "kkgfxm"
    - name: "云原生"
      repo: "ooa19f"
    - name: "DIY搞事情"
      repo: "bua6cb"
    - name: "开开脑洞"
      repo: "ussmi8"
Blog:
  title: "WangXun`s Blog"
  subtitle: "大道至简"
  keywords: "云原生,kubernetes,docker,golang,python,物联网,运维开发"
  description: ""
  author: "WangXun"
  links:
    - name: ""
      url: ""

三、踩坑指南

  • 静态资源的坑: 在写文章时直接黏贴图片,用高级功能画图到语雀是很好的体验,在语雀上看文档正常渲染也没问题。问题是,当实现了web服务器,成功代理拿到了文章的html代码并且返回,以为万事大吉的时候,打开页面发现所有的那些思维导图流程图还有帖进去的图片全是红叉叉,被语雀的防盗链干掉了。
  • 代码高亮没有了:见语雀官方的说明:https://www.yuque.com/yuque/developer/yr938f API导出来的格式基本就是markdown转换成html格式,代码高亮没了, 段落样式也没了,奇丑无比。


四、填坑

  • 针对静态资源的坑,通过替换html中的CDN连接,劫持到自己的博客后端来进行反向代理,成功解决, 核心代码:
func (y Config) ServeHTTP(uri string, w http.ResponseWriter, r *http.Request) {
	// 反向代理
	remote, err := url.Parse(uri)
	if err != nil {
		panic(err)
	}
	proxy := httputil.NewSingleHostReverseProxy(remote)
	d := proxy.Director
	proxy.Director = func(r *http.Request) {
		r.Header.Set("Referer", "")
		r.Host = remote.Host
		d(r)
	}
	proxy.ServeHTTP(w, r)
}


  • 代码高亮和样式问题: 下方是语雀官方提供了样式,但是这里的样式只负责段落格式,代码高亮未能解决:
<link rel="stylesheet" type="text/css" href="http://editor.yuque.com/ne-editor/lake-content-v1.css">
  • 安排了highlight.js来解决代码高亮的问题。
<link href="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.7.2/styles/atom-one-dark.min.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.7.2/highlight.min.js"></script>
<script>
    window.onload = function() {
        var aCodes = document.getElementsByTagName('pre');
        for (var i=0; i < aCodes.length; i++) {
            hljs.highlightBlock(aCodes[i]);
        }
    };
</script>


五、大功告成

  • 手撸html的极简风,就喜欢这么简单(其实是为了偷懒), 图中的四个分类,分别对应语雀的4个知识库。
首页
  • 文档列表: 列出的是语雀知识库下的文章列表:
  • 文章查看页面

六、开源计划

  • 已开源至:
  • 欢迎贡献模板