Golang 语言 vendor 在 GOPATH 和 Modules 中的区别

简介: Golang 语言 vendor 在 GOPATH 和 Modules 中的区别

01

介绍

在 Golang 语言中,Golang 程序是由 Golang Package 组成的,go build 的过程实际上就是编译 Golang Package。本文我们介绍 Golang 构建模式主要演进的三个阶段,分别是 GOPATH、 引入 vendor 机制的 GOPATH 和 Go Module。

GOPATH

Golang 初期版本中就原生内置了 GOPATH 的构建模式,Golang 程序在编译时,Golang 编译器会在 GOPATH 环境变量配置的本地路径下,查找 Golang 程序依赖的三方包,如果依赖包不存在,go build 命令将返回错误「无法找到包 XXX」,此时,我们需要使用 go get 命令手动将 Golang 程序依赖的三方包下载到 GOPATH 环境变量配置的本地路径下,然后再尝试执行 go build 命令。

go get 命令虽然方便,它可以将依赖包以及依赖包的依赖包自动下载到 GOPATH 环境变量配置的本地路径下,但是读者朋友们需要注意的是,go get 命令下载的依赖包是当前依赖包的最新版本,如果我们对依赖包的版本有要求,就不能使用 go get 命令。

比如在多人开发的 Golang 项目中,新加入成员将 Golang 项目下载到本地,使用 go get 命令下载依赖包时,正好赶上依赖包的版本更新了,此时,新成员使用 go build 命令构建 Golang 程序,Golang 程序也将使用最新版本的三方依赖包。如果三方依赖包存在 bug 或不向下兼容,将直接影响 Golang 程序的稳定性。

03

Vendor

Golang 官方为了解决 go get 命令会下载最新版本依赖包的问题,在 Golang v1.5 版本中引入 vendor 机制。

所谓 vendor 机制,就是在 Golang 项目的目录中,创建一个目录名为 vendor 的目录,将 Golang 项目的所有依赖包缓存到该目录中。

Golang 程序在编译时,Golang 编译器会优先在 vendor 目录中查找 Golang 程序依赖的三方包,而不是在 GOPATH 环境变量配置的本地路径下查找。

我们只需将 vendor 目录一起提交到代码仓库中,新成员在下载 Golang 项目后,构建项目就不会改变三方依赖包的版本。

读者朋友们需要注意的是,vendor 机制也引入了新的问题,比如想要使用 vendor 机制,Golang 项目必须在 GOPATH 环境变量配置的本地路径下的 src 目录中。

随着项目不断迭代,依赖的三方包也会越来越多,vendor 目录会变得越来越大,将 vendor 目录提交到代码仓库,不仅会影响下载代码的速度,还会影响 Code Review。

此外,vendor 目录中的三方依赖包,也需要我们手动管理,比如手动记录依赖三方包的版本号,手动下载三方依赖包等。

Golang 社区为了解决 vendor 机制引入的问题,推出一些比较流行的解决三方包依赖管理的工具,比如 dep、gb、glide 等三方包依赖管理工具,但是这些社区推出的三方包依赖管理工具都有各自的问题。

04

Modules

Golang 官方在总结 Golang 社区推出的各种三方包依赖管理工具遇到的问题的基础上,在 Golang v1.11 版本中,推出 Go Module 构建模式。

关于 Go Module 构建模式,官方 blog 有相关系列文章介绍,在之前的公众号文章中,也有官方 blog 发表的 Go Module 相关文章的译文。感兴趣的读者朋友可以按需翻阅一下。

Go Module 构建模式可以将 Golang 项目代码放在任意目录,无需同 vendor 机制一样,必须将 Golang 项目代码放在 GOPATH 环境变量配置的本地目录下的 src 目录中。

因为 Golang 官方为了同时支持 GOPATH 构建模式和 Go Module 构建模式,在 Golang v1.11 版本中,Go Module 构建模式默认是「关闭」,除非手动开启 Go Module 构建模式,如果将 Go Module 构建模式设置为「自动」,而 Golang 项目在 GOPATH 环境变量配置的目录中的 src 目录下,go build 命令优先使用 GOPATH 构建模式。

在 Golang v.13 版本中,Go Module 构建模式默认是「自动」,不管 Golang 项目在不在 GOPATH 环境变量配置的本地目录中的 src 目录下,只要项目根目录中包含 go.mod 文件,就启用 Go Module 构建模式,否则启用 GOPATH 构建模式。

所以在 Golang v1.13 版本之前,如果想要使用 Go Module 构建模式,那么 Golang 项目代码不可以放在 $GOPATH/src 目录中。

在 Golang v1.16 版本开始,Golang 已经默认开启 Go Module 构建模式,未来 Golang 官方还会考虑彻底移除 GOPATH 构建模式,我建议读者朋友们现在开始将 GOPATH 构建模式的老项目迁移到 Go Module 构建模式,并且在新项目中直接使用 Go Module 构建模式。

Golang 项目使用 Go Module 构建模式,那么还需要使用 vendor 机制吗?答案是需要,因为 vendor 机制可以将 Golang 项目依赖的三方包缓存到 vendor 目录,这样在无法访问网络的环境下,我们可以在 vendor 机制下使用 Go Module 构建模式,或者在构建性能敏感的环境中使用 Go Module 构建模式,比如在使用内部 CI 工具构建 Golang 程序时。

在 Go Module 构建模式下,vendor 机制无需像在 GOPATH 构建模式下,需要我们手动管理三方依赖包的版本和下载,Golang 提供了 go mod vendor 命令,帮助我们创建和管理 vendor 目录。

在我们想要基于 vendor 目录缓存的三方依赖包构建 Golang 程序,而不是基于本地缓存的 Go Module 构建 Golang 程序时,可以在 go build 命令后面加上 -mod=vendor 参数。并且在 Golang v1.14 及以后的版本中,如果 Golang 项目根目录下存在 vendor 目录,go build 命令会默认优先基于 vendor 目录缓存的三方依赖包构建 Golang 程序,除非我们在 go build 命令后面加上 -mod=mod 参数。

05

总结

本文我们介绍了 Golang 的构建模式的演进过程,Golang 官方先后推出三种构建模式,分别是 GOPATH 构建模式,引入 vendor 机制的 GOPATH 构建模式和 Go Module 构建模式。

介绍了为什么要引进 vendor 机制,它解决了什么问题,同时它又带来了什么问题。官方基于社区推出的三方依赖包版本管理工具的基础上推出 Go Module 构建模式,是怎么解决 GOPATH 构建模式中 vendor 机制引入的问题的。

介绍了 vendor 机制在 GOPATH 构建模式和在 Go Module 构建模式中使用的区别是什么,为什么在 Go Module 构建模式中仍然需要使用 vendor 机制,如何在 Go Module 构建模式中使用 vendor 机制。

推荐阅读:

参考资料:

https://go.googlesource.com/proposal/+/master/design/25719-go15vendor.md 

https://pkg.go.dev/cmd/go#hdr-GOPATH_and_Modules 


目录
相关文章
|
1月前
|
SQL 前端开发 Go
编程笔记 GOLANG基础 001 为什么要学习Go语言
编程笔记 GOLANG基础 001 为什么要学习Go语言
|
3月前
|
物联网 Go 网络性能优化
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式【1月更文挑战第21天】【1月更文挑战第104篇】
101 1
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
6 1
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
10 1
|
2天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
3天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
11 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
3天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
17 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
|
4天前
|
Go
Golang深入浅出之-Go语言基础语法:变量声明与赋值
【4月更文挑战第20天】本文介绍了Go语言中变量声明与赋值的基础知识,包括使用`var`关键字和简短声明`:=`的方式,以及多变量声明与赋值。强调了变量作用域、遮蔽、初始化与零值的重要性,并提醒读者注意类型推断时的一致性。了解这些概念有助于避免常见错误,提高编程技能和面试表现。
19 0
|
1月前
|
Go 开发工具 git
编程笔记 GOLANG基础 003 Go语言开发环境搭建
编程笔记 GOLANG基础 003 Go语言开发环境搭建
|
1月前
|
存储 Java Go
编程笔记 GOLANG基础 002 Go语言简介
编程笔记 GOLANG基础 002 Go语言简介

相关实验场景

更多