`go mod vendor` 命令的目的是什么?

what is the purpose of `go mod vendor` command?

documentation 说,

The go mod vendor command constructs a directory named vendor in the main module's root directory that contains copies of all packages needed to support builds and tests of packages in the main module. Packages that are only imported by tests of packages outside the main module are not included.

golangbyexample 说:

You can also check in the vendor directory to your VCS (Version Control System). This becomes useful in sense that none of the dependency needs to be downloaded at run time as it is already present in the vendor folder checked into VCS

我认为模块 (go.mod,go.sum) 负责版本控制。我还认为只有当我第一次 运行 程序时才会下载依赖项。

那么,命令 go mod vendor 有什么用呢?它的目的或用例是什么?

Go Modules 负责版本控制,但不一定负责模块从 Internet 上消失或 Internet 不可用。如果模块不可用,则无法构建代码。

Go Proxy 会通过镜像模块在一定程度上缓解消失的模块,但它可能不会一直对所有模块都这样做:

Why did a previously available module become unavailable in the mirror?

proxy.golang.org does not save all modules forever. There are a number of reasons for this, but one reason is if proxy.golang.org is not able to detect a suitable license. In this case, only a temporarily cached copy of the module will be made available, and may become unavailable if it is removed from the original source and becomes outdated. The checksums will still remain in the checksum database regardless of whether or not they have become unavailable in the mirror.

查看更多信息:https://proxy.golang.org/

另一种方法是分叉模块并使用 Go Modules replace directive which allows redirecting an import path to another, e.g. your fork, in the go.mod file without changing your code. This approach was provided by colm.anseo.

关于 Internet 访问,如果您 运行 一个大型服务器场并且需要在多台机器上使用代码,从 Internet 下载到您场中的每台机器可能效率低下并且存在安全风险。在内部仓库中使用 go mod vendor 并复制它可能会更有效。大公司使用内部方法将代码部署到数据中心的多个服务器。

这个问题给了我一个思路。有时我想知道一个模块有多“臃肿” 是的,所以我想要一种方法来查看模块的大小,但也可以查看任何第三方 方进口。为此,我编写了下面的脚本,利用 go mod vendor:

package main

import (
   "bytes"
   "fmt"
   "io/fs"
   "os"
   "os/exec"
   "path/filepath"
   "strings"
)

func count(mod string) (int, error) {
   imp := fmt.Sprintf("package main\nimport _%q", mod)
   os.WriteFile("size.go", []byte(imp), os.ModePerm)
   exec.Command("go", "mod", "init", "size").Run()
   exec.Command("go", "mod", "vendor").Run()
   var count int
   filepath.WalkDir("vendor", func(s string, d fs.DirEntry, err error) error {
      if strings.HasSuffix(s, ".go") && !strings.HasSuffix(s, "_test.go") {
         data, err := os.ReadFile(s)
         if err != nil {
            return err
         }
         count += bytes.Count(data, []byte{'\n'})
      }
      return nil
   })
   return count, nil
}

示例:

package main

func main() {
   c, err := count("github.com/corona10/goimagehash")
   if err != nil {
      panic(err)
   }
   println(c)
}