# 29|周边:框架发布和维护也是重要的一环 你好,我是轩脉刃。 终于来到框架设计与完善的最后一节课了。在前面的章节中,我们基本上把框架的功能都开发完成了,但是这只是万里长征的第一步。一个工业级的Web框架一定是经过长时间千锤百炼的迭代升级的。在这门课编写完成的时候,我为hade框架锁定了v 1.0.0版本,后续我们会继续为框架增加更多的功能和特性。 那么随着框架的不断更新和升级,随之而来的问题就是如何为一个开源项目设计一套发布和使用机制,并且为每个发布版本维护一套准确的框架说明文档?这就是我们今天要讨论的内容。 ### 版本 每个框架发布都需要有一个版本号,这个版本号如何定义,我们在前面的课程中已经不止一次提到过了,这里再正式说明一下。 所有开源软件的版本号基本上都遵循“[语义化版本规范](https://semver.org/lang/zh-CN)”的约定,这份语义化版本规范是由Gravatars 创办者兼 GitHub 共同创办者 Tom Preston-Werner 所建立的,它定义了三段式的版本规范,格式如下: ```plain 主版本号.次版本号.修订号 ``` 我们使用的Golang语言项目也是基于这个规范来实现的。 **主版本号代表如果做了不兼容的API修改**。比如在你的项目中,原先提供的A方法要替换为B方法,所有的参数和返回值都已经变化了,你的使用方必须修改他的代码,这个变动就叫做“不兼容的API修改”。这个时候,主版本号就必须更新了。 **次版本号表示当你做了向下兼容的功能性新增**。比如原先你的类中只有A方法,当你新增了一个B方法,但是并不修改原先的A方法,这个时候你的类有A、B两个方法。原先的库使用者并不需要更新他的任何代码,这就是“向下兼容的功能性新增”。这个时候,你不需要更新主版本号,只需要更新次版本号就行。 **修改号,表示当你做了向下兼容的问题修正**。比如原先类中只有A方法,你并没有更新任何的功能,只是修改了A方法中的一个bug,那么这个时候,不需要更新主版本号和次版本号,直接更新修订版本号就行。 这个语义化版本规范,基本上已经是开源届的共识了,当然也可以不遵循这个规范,但是一旦不遵循,对你开源项目的使用者来说绝对是一个灾难性的事情,进而你的项目也逐渐会失去使用者。 我们使用的Go语言版本就是严格遵循这个规范的。比如现在使用的Golang版本为1.17.2。主版本为1,次版本为17,修改号为2。基本上我们接触Golang都是从1.0.0之后开始的,目前Go还没发布2.0.0,所以1.0之后的Go版本升级都是向下兼容的。如果有做过Go升级的同学可以思考下,在升级的时候,业务代码有没有做任何变动。 另外再附带说一下,你也有可能在网络上看到版本号不止三个的,比如1.2.3.4,或者 1.2.3.beta。这些都是对这个语义化规范的扩展使用而已,最后一个字段根据不同的项目可能有不同的意思,比如是否是发行版本。不过前三个版本号都是遵循这个版本规范的。所以见到这些扩展版本号不要吃惊。 我们的hade框架当然也要基于这个版本规范,在这门课程更新完成之后,我会把这门课程的所有代码锁定为一个发布版本1.0.0,并且从这个版本开始继续迭代更新功能。有新的功能和模块增加,我们会升级次版本号,有任何的bug修复,就升级修订版本号。 ### 发布 明确了hade的版本号规范之后,要明确我们的发布模式,这个在前面的[第22节课](https://time.geekbang.org/column/article/435534)开发自动化脚手架已经简单说过了,这里再正式说明一下。 我们的框架最终在GitHub上的地址为:[https://github.com/gohade/hade](https://github.com/gohade/hade) ,会至少为每一个次版本号打上release版本。 ![](https://static001.geekbang.org/resource/image/ca/75/caec1bcef876ba937a5eacf036e58975.png?wh=1920x1025) 而对于框架的使用者来说,使用步骤会是这样的。首先需要在工作机器上安装hade命令,我们可以在任何路径调用: ```go go install github.com/gohade/hade@latest ``` 来安装hade命令。这个hade命令,其实用任何版本都是可以的,这里直接选用最新版本。 调用了go install 之后,hade命令就被安装到你的$GOPATH/bin目录下了。直接调用 `$GOPATH/bin/hade` 可以看到安装好的hade命令。 ![](https://static001.geekbang.org/resource/image/b8/c0/b8eec8f059ac82a5a5f1de6657dba7c0.png?wh=1920x1172) 然后使用 `hade new` 命令创建一个项目,比如为hellohade。我们可以看到创建出了目标文件夹hellohade。 ![](https://static001.geekbang.org/resource/image/c4/fc/c4de7585ff0c36e79ef038bcdf952bfc.png?wh=1920x1312) 进入目标文件夹 `cd hellohade` ,调用 `go mod tidy` 下载所有的依赖包,调用 `go build` 编译目录。 ![](https://static001.geekbang.org/resource/image/03/92/030ec01edf75260c35782db1413ed192.png?wh=1566x1044) 接下来,使用者就可以在这个目标文件夹中开始基于hade开发应用了。 ![](https://static001.geekbang.org/resource/image/ea/8e/ea74acc44f15623d50dd4608e1cyy48e.png?wh=1920x1320) ## 文档维护 到这里,我们把框架的版本和发布的流程梳理清楚了。但是随着版本不断迭代更新,框架对应的说明文档也是需要不断更新的,这就涉及框架文档的编写和搭建了。 对于一个开源项目,说明文档的查看渠道是多种多样的。 对于比较小型的项目和类库,一般都会选择直接使用markdown来编写。比如我们在之前用到的 [go-daemon](https://github.com/sevlyar/go-daemon) 库,功能比较简单,就是创建一个daemon进程,它的使用方式也比较简单,所以,作者就在项目GitHub地址的 README.md 中写所有信息的使用文档了。 而对于比较大型的项目,大多数项目都会自己开启一个官方网站,来说明项目的各种使用方法,比如[gorm](https://github.com/go-gorm/gorm) 项目,这个库封装的函数多种多样,所以作者单独创建了一个[网站](https://gorm.io/zh_CN/docs/index.html)来列出对gorm项目的说明。 使用网站来展示项目使用文档有一个额外工作,就是当版本升级的时候,要同时升级官方文档。否则的话,使用者就会经常遇到看旧文档、使用新框架的情况,而导致错误的用法。所以必须及时维护代码和文档的一致性。 那有没有办法将这两者结合一下呢?我们使用markdown编写说明文档,然后如果能自动将这些markdown文档转化为HTML网站,再将网站部署到服务器上,那就完美了。 确实是有这样的工具的,[vuepress](https://vuepress.vuejs.org/zh/guide)。vuepress是一个Vue工具,基于Vue框架生成了一个vuepress的命令行工具,这个工具能将指定的markdown文件转化为HTML文件,而这个HTML文件是可以直接被使用者访问的。 **vuepress的编译需要两个目录,存放markdown的目录和生成HTML的目标目录**。vuepress是基于Vue的,正好hade框架也已经融合了Vue。所以自然可以想到,直接将存放markdown的目录放在hade框架地址上,然后将生成HTML的目标目录定义为我们的dist目录。这样,就可以在hade框架上使用npm工具来生成HTML了。 ## 编写markdown 于是我们在根目录下创建docs目录,存放编写的markdown文件。 这里框架说明的markdown文件我都事先编写完成,也存放在GitHub上了,你可以比对查看。vuepress的markdown文件格式,编写并没有什么特别,只需要你多阅读尝试,心里对哪种格式最终的页面展现是什么样子有理解就行。 比如想要首页的展示形式是这样: ![](https://static001.geekbang.org/resource/image/ca/2f/cae7c6eb3fc8971f5e4534538e78232f.png?wh=1920x589) 它对应的markdown为dos/README.md: ```markdown --- home: true actionText: 开始体验 actionLink: /guide/introduce footer: MIT Licensed | Copyright © 2020-present jianfengye features: - title: 基于协议 details: 服务与服务间的协议是基于协议进行交互的。 - title: 前后端协同 details: 前后端协同开发 - title: 命令行 details: 有充分的命令行工具 - title: 集成定时服务 details: 如果你需要启动定时服务,提供命令进行定时服务的启动 - title: 文档丰富 details: 提供丰富的文档说明,提供丰富的文档说明 - title: 开发模式 details: 在开发模式下进行前后端开发,极大提高了开发效率和开发体验 ``` 根据vuepress的官方文档,我们需要在docs目录下创建一个.vuepress/config.js ,来给vuepress工具阅读,也就是用来告诉vuepress工具,你需要按照这个配置的信息生成HTML文件。这里的所有配置都在[官网](https://vuepress.vuejs.org/zh/config)有说明。 我们的config.js的配置如下: ```javascript module.exports = { title: "hade框架", // 设置网站标题 description: "一个支持前后端开发的基于协议的框架", //描述 dest: "./dist/", // 设置输出目录 port: 2333, //端口 base: "/v1.0/", head: [["link", {rel: "icon", href: "/assets/img/head.png"}]], themeConfig: { //主题配置 // logo: "/assets/img/head.png", // 添加导航栏 nav: [ {text: "主页", link: "/"}, // 导航条 {text: "使用文档", link: "/guide/"}, {text: "服务提供者", link: "/provider/"}, { text: "github", // 这里是下拉列表展现形式。 items: [ { text: "hade", link: "https://github.com/gohade/hade", }, ], }, ], // 为以下路由添加侧边栏 sidebar: { "/guide/": [ { title: "指南", collapsable: false, children: [ "introduce", "install", "build", "structure", "app", "env", "dev", "command", "cron", "middleware", "swagger", "provider", "todo", ], }, ], "/provider/": [ { title: "服务提供者", collapsable: false, children: [ "app", "env", "config", "log", ], }, ], }, }, }; ``` 说明下几个重点配置项。 dest这个配置项指定了我们生成HTML的目标文件夹,这里定义为dist目录。base代表所有页面的访问前缀,我们定义为和版本号一致的/v1.0/。这样设置最终会有什么效果呢? 由于hade会有多个版本,而我们希望访问地址的URL中带着版本信息,这样就能通过URL来访问不同的版本信息。比如hade.funaio.cn/v1.0/,访问v1.0的版本信息;hade.funaio.cn/v1.1/ 访问v1.1的版本信息。所以使用/v1.0/的base,能让所有的访问信息都带上这个前缀,就能访问到不同版本的框架信息了。 themeConfig.nav是配置导航栏的,我们的导航栏有四个信息。 ![](https://static001.geekbang.org/resource/image/f7/59/f72cd0a32e3d76bf5b67798dbe495e59.png?wh=318x52) 所以在themeConfig.nav中需要定义四个子项,子项目的text表示显示文本,而link表示点击这个文本之后的链接地址。比如: ```javascript {text: "使用文档", link: "/guide/"}, ``` 表示“使用文档”这个文本点击之后,会查找/guide/目录下的README文件。 而/guide/目录下的所有文件是通过themeConfig.sidebar设置侧边栏的,也就是刚才代码的第29到47行。最终通过这段的设置,点击首页的“使用文档”链接,就会进入如下的效果: ![](https://static001.geekbang.org/resource/image/c7/16/c72c2edcdc7f4ed9abe098a3285ae016.png?wh=1308x638) 左边的每个链接都对应docs/guide/中的每个markdown文件。 ### 生成HTML docs下的markdown文件都编写完成了,下面我们就安装vuepress并且生成HTML。安装vuepress,只需要使用npm命令: ```go npm install -D vuepress ``` 就能安装最新版本的vuepress。截止11月13日,目前vuepress的2.0还处在beta版本,最稳定版本是1.8.2。在安装过程中你可能会遇到这个错误: ```plain TypeError: Cannot read property 'createHash' of undefined ``` 这个错误是提示我们的Webpack版本较低,使用命令升级Webpack就行。 ```plain npm i webpack@4.8.3 ``` 安装好vuepress之后,我们就可以在package.json中设置vuepress的调试和编译命令了: ```go { "name": "hade", ... "scripts": { ... "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, ``` 增加了docs:dev 和 docs:build 来运行vuepress。 这里我们使用 `npm run docs:build` ,就能生成对应markdown的HTML了,非常方便。 ![](https://static001.geekbang.org/resource/image/85/37/85a2882ca4500282f43d23110fc07537.png?wh=698x518) 可以看到dist目录中已经生成了对应的HTML文件: ![](https://static001.geekbang.org/resource/image/f0/9b/f0671ba855661eb73fb4c4a49f65ed9b.png?wh=321x168) 最后将这个HTML文件部署到Web服务器中。这里我们部署在目标服务器的的/webroot/hade\_doc/dist\_1.0目录中。具体部署方法也没有什么难点,通过FTP或者SETP上传dist目录内容到目标目录即可。 我们的目标服务器配置的Web服务器为Nginx,Nginx如何配置,你可以参考: ```plain     server {         server_name  hade.funaio.cn;         access_log  logs/hade.access.log  main;         error_log  logs/hade.error.log ;         location /v1.0/ {             alias /webroot/hade_doc/dist_1.0/;             index  index.html index.htm;         }         location / {             root   /webroot/hade_doc/dist_1.0/;             index  index.html index.htm;         }     } ``` 配置hade.funaio.cn/v1.0/ 来访问目录路径 /webroot/hade\_doc/dist\_1.0/ ,同时hade.funaio.cn/也是访问/webroot/hade\_doc/dist\_1.0/。 这样每次我们的版本有更新的时候,创建一个新的目标目录存放这个版本的HTML,比如 /webroot/hade\_doc/dist\_1.1/。而在nginx上,只需要增加一个路径/v1.1/来提供hade.funaio.cn/v1.1/的访问路径,让使用者访问v1.1的文档。 于是,我们hade框架的网站文档:[http://hade.funaio.cn](http://hade.funaio.cn) 就正式搭建起来了。 ![](https://static001.geekbang.org/resource/image/c2/6c/c235a8ce622fdfa33e6dc382bfac566c.png?wh=1920x855) 看到这里,相信你应该理解了,我们的文档维护是基于hade项目中自带的docs/ 目录下的markdown 来进行的。你可以通过GitHub上的[markdown](https://github.com/gohade/coredemo/blob/geekbang/29/README.md) 来查看框架文档,也可以通过网站 hade.funaio.cn 来查看框架文档。这两者本质上都是通过markdown来编写的。 而markdown会随着hade框架的发布而发布,同时网站也是根据markdown生成的。这种方法既能避免文档和框架不一致的问题,又能大大降低维护网站的成本。 这节课我们没有修改框架的Go代码,主要创建了包含markdown文件的docs目录。目录截图放在这里供你你参考。所有代码也同步到了[geekbang/29](https://github.com/gohade/coredemo/tree/geekbang/29) 分支上了。 ![](https://static001.geekbang.org/resource/image/c0/25/c0c15e07407e6a7102913446ed46b725.png?wh=361x670) ## 小结 今天我们为框架的升级和维护设计了一套完整的方案。hade框架的发布和文档维护都有自己的独特设计,比如框架的发布,直接和hade框架的命令行工具进行了关联,只要发布了一个新版本,使用者就能直接用命令行工具使用这个新版本创建一个脚手架。而文档维护,是通过编写markdown以及hade框架已经集成的Vue,来自动生成网站HTML。 截止到这节课,hade框架的功能就开发完毕了,GitHub上的coredemo项目也就不再更新了,之前的所有代码,我们都会移动到真正的hade项目的 [https://github.com/gohade/hade](https://github.com/gohade/hade) 地址,并且我们为代码发布了发布版本:v1.0.0,后续会基于这个版本不断迭代更新,如果你有兴趣,欢迎一起来完善这个框架。 ### 思考题 马上我们会进入实战环节,使用hade框架开发具体应用,会使用到Vue和Element-UI的知识,当然在后面我们也会稍微介绍一些前端知识,不过如果你之前完全没有接触过,可以花一些时间预习一下,这样学习的时候才能事半功倍。 所以今天就给你分享一些相关资料: * [vue中文官网](https://cn.vuejs.org/index.html),学习Vue必看内容 * [element-ui官网](https://element.eleme.io/#/zh-CN),一款快速搭建网站的UI框架,目前国内最火 * [awesome-vue](https://github.com/rumengkai/awesome-vue),收集Vue的一些优秀开源项目,绝对收藏 * [vue router官网](https://router.vuejs.org/zh),Vue的路由必备 * [webpack官网](https://v4.webpack.docschina.org/concepts),Vue的打包工具 欢迎在留言区分享你的学习笔记。感谢你的收听,如果你觉得今天的内容对你有所帮助,也欢迎分享给你身边的朋友,邀请他一起学习。我们实战篇见。