如同每年写前端的时候都要学习新框架一样,几乎每次写新的golang项目,我们都能学到新的依赖管理方式。 --埃里克.杨

Go在最新1.11版本,正式启用了真官方开发的包管理工具module,已经集成到go命令中,它以前的名字叫vgo

要是想快速弄懂这东西怎么玩,看官方wiki里啰啰嗦嗦的介绍肯定要被光速劝退的,本篇的内容就是啃完这个wiki,并把重要的部分提取出来(最后依然被劝退)。其实要学会它的用法不应该看官方文档,而是看官方老哥的这篇文章A Tour of Versioned Go (vgo)

dep的开发者和go官方团队还有一段撕逼,感觉吃瓜比写代码有趣的样子。

使用

初始化

在哪用?你一定要在GOPATH外面创建你的项目。(没错,这门逗比语言又在教育你项目目录应该放在哪了)如果你想在GOPATH里用,需要先设置GO111MODULE=on

  • 进入到项目中,用go mod init github.com/you/hello

init后面那段东西是你的包名。它会帮你创建一个go.mod文件,这个文件会被go工具链接管,一般来说不需要手工修改它,当你用go get之类的命令时,它会帮你修改这个文件。

添加/指定依赖

通过go get命令来添加依赖,依赖的包及其版本会被记录在go.mod,包名、版本及哈希值会被记录到go.sum这个文件中。注意,这个go.sum并不是一个lock文件。

  • 添加依赖: go get github.com/gorilla/mux
  • 添加特定版本: go get github.com/gorilla/mux@v1.6.2
  • 添加特定版本,限定到版本范围: go get github.com/gorilla/mux@'<v1.6.2'
  • 添加特定版本,限定到特定git commit: go get github.com/gorilla/mux@e3702bed2

升级依赖

  • 升级major版本: 请使用上面指定版本的方式来装
  • 升级全部依赖的minor或patch版本: go get -u
  • 升级全部依赖的patch版本: go get -u=patch

补全/清理依赖

  • go mod tidy 这个命令会下载缺失依赖,并清理无用的依赖(包括清理go.mod中的记录)

概念

模块 Module

模块是一个包及其依赖的一系列特定版本的包所形成的集合。模块中记录有明确的版本依赖。通常一个git仓库的项目就是一个模块,但也可以在同一个仓库中维护多个模块。模块采用语义版本来定义版本号。

语义版本 Semantic versioning

v(major).(minor).(patch)

  • major: 做了不兼容的升级,升这个
  • minor: 向前兼容的升级,升这个
  • patch: 未改变功能,只是修了bug之类的,升这个
  • 开头的那个v是不能省略掉的
  • 在import的包主版本号v0或v1时,import路径不能写版本号
  • 在import的包主版本号v2或更高的时候,需要在路径里加主版本号v2

go.mod语法

  • module: 本模块的路径,所有本模块下的包共享这个路径前缀
  • require: 定义所依赖的模块
  • replace: 替换import模块的路径
  • exclude: 排除某个模块,使之不能被import

坑在哪

虽然看起来一切都很美好,但现实是,一但脱离了GOPATH,就要以一种奇怪的方式来import模块或者在go.mod里调整import路径才能使之正常工作,而官方文档对于正确import的姿势描述得也不够详细。语义版本会影响引用路径,在不同版本时用法不一致,比较违反直觉,另外如果依赖的外部模块开发时没有按照这一套语义版本机制来发布,也可能会出现一些问题。总的来说使用起来并不直观和自然,在刚上手的时候要花不少时间来研究这套机制是如何工作的。

总结

不要用这辣鸡玩意,老老实实回到GOPATH里使用glide 或者dep更加合适一些,体验和npm、pipenv或cargo差不多。人生苦短,别把生命浪费在依赖怎么装上面,据官方文档所说,这个go mod目前也是一个实验特性(说不定哪天又被砍掉了)不如等以后这东西成熟了再来用。

当然更可能是我太菜了,玩不转这一套高科技(为什么不试试神奇的Rust呢?与生命周期斗其乐无穷

参考