背景

Go 1.18除了引入泛型(generics)、含糊测试(Fuzzing)之外,另外一个重大性能是引入了工作区模式(workspace mode)。

工作区模式对本地同时开发多个有依赖的Module的场景十分有用。

举个例子,咱们当初有2个Go module我的项目处于开发阶段,其中一个是example.com/main,另外一个是example.com/util。其中example.com/main这个module须要应用example.com/util这个module里的函数。

咱们来看看Go 1.18版本前后如何解决这种场景。

Go 1.18之前怎么做

在Go 1.18之前,对于上述场景有2个解决计划:

计划1:被依赖的模块及时提交代码到代码仓库

这个计划很好了解,既然example.com/main这个module依赖了example.com/util这个module,那为了example.com/main能应用到example.com/util的最新代码,须要做2个事件

  • 本地开发过程中,如果example.com/util有批改,都马上提交代码到代码仓库,而后打tag
  • 紧接着example.com/main更新依赖的example.com/util的版本号(tag),能够应用go get -u命令。

这种计划比拟繁琐,每次example.com/util有批改,都要提交代码,否则example.com/main这个module就无奈应用到最新的example.com/util

计划2:go.mod里应用replace指令

为了解决方案1的痛点,于是有了计划2:在go.mod里应用replace指令。

通过replace指令,咱们能够间接应用example.com/util这个module的本地最新代码,而不必把example.com/util的代码提交到代码仓库。

为了不便大家了解,咱们间接上代码。代码目录构造如下:

module||------main|        |---main.go|        |---go.mod        |------util|        |---util.go|        |---go.mod

main目录下的main.go代码如下:

//main.gopackage mainimport (    "fmt"    "example.com/util")func main() {    result := util.Add(1, 2)    fmt.Println(result)}

main目录下的go.mod内容如下:

module example.com/maingo 1.16require example.com/util v1.0.0replace example.com/util => ../util

util目录下的util.go代码如下:

// util.gopackage utilfunc Add(a int, b int) int {    return a + b}

util目录下的go.mod内容如下:

module example.com/utilgo 1.16

这里最外围的是example.com/main这个module的go.mod,最初一行应用了replace指令。

module example.com/maingo 1.16require example.com/util v1.0.0replace example.com/util => ../util

通过replace指令,应用go命令编译代码的时候,会找到本地的util目录,这样example.com/main就能够应用到本地最新的example.com/util代码。进入main目录,运行代码,后果如下所示:

$ cd main$ go run main.go3

然而这种计划也有个问题,咱们在提交example.com/main这个module的代码到代码仓库时,须要删除最初的replace指令,否则其余开发者下载后会编译报错,因为他们本地可能没有util目录,或者util目录的门路和你的不一样。

代码开源地址:Go 1.18之前应用replace指令

Go 1.18工作区模式

为了解决方案2的痛点,在Go 1.18里新增了工作区模式(workspace mode)。

该模式下不再须要在go.mod里应用replace指令,而是新增一个go.work文件。

话不多说,间接上代码。代码目录构造如下:

workspace|------go.work||------main|        |---main.go|        |---go.mod        |------util|        |---util.go|        |---go.mod

main目录下的main.go代码如下:

//main.gopackage mainimport (    "fmt"    "example.com/util")func main() {    result := util.Add(1, 2)    fmt.Println(result)}

main目录下的go.mod内容如下:没有了计划2里最初一行的replace指令

module example.com/maingo 1.16require example.com/util v1.0.0

util目录下的util.go代码如下:

// util.gopackage utilfunc Add(a int, b int) int {    return a + b}

util目录下的go.mod内容如下:

module example.com/utilgo 1.16

go.work内容如下:

go 1.18use (    ./main    ./util)

在workspace目录下执行如下命令即可主动生成go.work

$ go1.18beta1 work init main util

go1.18beta1 work init前面跟的mainutil都是Module对应的目录。

如果go命令执行的当前目录或者父目录有go.work文件,或者通过GOWORK环境变量指定了go.work的门路,那go命令就会进入工作区模式。在工作区模式下,go就能够通过go.work下的module门路找到并应用本地的module代码。

在main目录或者workspace目录,都能够运行main.go,后果如下所示:

$ go1.18beta1 run main/main.go 3$ cd main/$ go1.18beta1 run main.go3

这种模式下,咱们对example.com/main 没有任何本地侵入性批改,不必像计划2那样,提交代码前还须要更新go.mod文件。example.com/main里的内容都能够间接提交到代码仓库。

代码开源地址:Go 1.18工作区模式

留神go.work不须要提交到代码仓库中,仅限于本地开发应用。

总结

为了解决多个有依赖的Module本地同时开发的问题,Go 1.18引入了工作区模式。

工作区模式是对已有的Go Module开发模式的优化,对于工作区模式的更多细节能够参考本文最初的References。

开源地址

文章和示例代码开源在GitHub: Go语言高级、中级和高级教程。

公众号:coding进阶。关注公众号能够获取最新Go面试题和技术栈。

集体网站:Jincheng's Blog。

知乎:无忌。

References

  • Proposal提案:https://go.googlesource.com/p...
  • workspace官网教程:https://go.dev/doc/tutorial/w...
  • workspace语法:https://go.dev/ref/mod#go-wor...
  • go work命令手册:https://pkg.go.dev/cmd/go@mas...
  • go 1.18 release notes: https://tip.golang.org/doc/go...
  • Go如何援用本地Module:https://github.com/jincheng9/...
  • polarisxu: https://polarisxu.studygolang...