Go 编程 | 连载 21 - Go Modules 和 Package

1,528 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

一、GOPATH 与 Go Modules

GOPATH 开发模式

GOPATH 是 Go 语言中使用的一个环境变量,它使用绝对路径提供项目的共工作目录,GOPATH 适合处理大量 Go 语言源码、多个包组合而成的复杂工程。

在命令行中输入 go env 可以查看 Go 的环境变量相关信息

image.png

Go 在 1.12 版本之前没有包管理的概念,所有的代码都保存在 GOPATH 的 src 目录下。

经过 go buildgo install 或者 go get 指令后,会将产生的二进制可执行文件存放在 GOPATH 的 bin 目录下,生成的中间缓存文件会保存在 GOPATH 的 pkg 目录下。

Go Modules 开发模式

Go Modules 是 Go 1.11 版本之后官方推出的版本管理工具,并且从 Go 1.13 版本开始为默认的依赖管理工具。

Go 1.11 版本开始在 GOPATH 以外的目录使用 go.mod 创建项目。使用 Go Module 管理的项目根目录中会包含 go.mod 文件,该文件为项目的依赖。

以一个 Gin Web 项目的 go.mod 为例,包含的依赖如下:

module gin-quickstart

go 1.18

require (
   github.com/gin-contrib/sse v0.1.0 // indirect
   github.com/gin-gonic/gin v1.8.1 // indirect
   github.com/go-playground/locales v0.14.0 // indirect
   github.com/go-playground/universal-translator v0.18.0 // indirect
   github.com/go-playground/validator/v10 v10.11.0 // indirect
   github.com/goccy/go-json v0.9.10 // indirect
   github.com/json-iterator/go v1.1.12 // indirect
   github.com/leodido/go-urn v1.2.1 // indirect
   github.com/mattn/go-isatty v0.0.14 // indirect
   github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
   github.com/modern-go/reflect2 v1.0.2 // indirect
   github.com/pelletier/go-toml/v2 v2.0.2 // indirect
   github.com/ugorji/go/codec v1.2.7 // indirect
   golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
   golang.org/x/net v0.0.0-20220726230323-06994584191e // indirect
   golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 // indirect
   golang.org/x/text v0.3.7 // indirect
   google.golang.org/protobuf v1.28.0 // indirect
   gopkg.in/yaml.v2 v2.4.0 // indirect
)

其中 module 是用来定义包名,require 表示定义依赖包及版本,而 indirect 表示间接引用。

go mod 常用命令如下:

  • go mod download:下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
  • go mod edit:编辑go.mod文件
  • go mod graph:打印模块依赖图
  • go mod init:初始化当前文件夹, 创建go.mod文件
  • go mod tidy:增加缺少的module,删除无用的module
  • go mod vendor:将依赖复制到vendor下
  • go mod verify:校验依赖
  • go mod why:解释为什么需要依赖

使用 Go Modules 创建项目有两种方式,第一种方式是可以直接使用 Goland IDE 选择 Go Modules 模式创建项目。

image.png

也可以直接创建一个文件夹作为工作目录,进入文件夹之后使用 go mod init 工作目录名 来初始化,该命令会生成一个 go.mod 文件来管理项目相关依赖。

二、Go Package

包(Package)是多个 Go 源码的集合,是一种高级代码复用方案,Go 语言默认提供了很多包,如 fmtosio 等。代码中用到相关包会自动导入。

同变量一样,如果包导入了但是没有使用,IDE 会有报错提示。

包具有如下特性:

  • 一个目录下的同级文件归属一个包
  • 包名可以与其目录不同名
  • 包名为 main 的包为应用程序的入口包,编译源码没有 main 包时将无法编译输出可执行文件。

Go 语言区分到小写,只有变量名或者结构体及结构体字段首字母大写才可以被其他包引用。

Go 中包的导入分为单行和多行导入

import "package1"
import "package2"
import (
    "package1"
    "package2"
)

新建一个 zulu package,首先在该 package 下新建一个 main 文件,接着再新建一个 operator package,在 operator package 下添加两个 add.go 和 sub.go 文件,zulu package 目录结构如下:

.
├── go.mod
├── main.go
└── operator
    ├── add.go
    ├── go.mod
    └── sub.go

1 directory, 5 files

其中 add.go 和 sub.go 文件的内容如下

// filename: add.go
package operator

func Add(x, y int) int {
   return x + y
}

func addWithlowerCase(x, y int) int {
   return x + y
}
// filename:sub.go

package operator

func Sub(x, y int) int {
   return x - y
}

然后我们可以在 main 文件中调用 operator 包下的函数

package main

import "zulu/operator"

func main() {

   operator.Add(1, 2)
   operator.Sub(2, 1)
   // Unexported function 'addWithlowerCase' usage
   // operator.addWithlowerCase(1, 2)
}

如果运行出现 package xxx is not in GOROOT (/usr/local/go/src/xxx) 报错,解决此问题首先要确保是在 Go Mod 模式下进行开发的,既要在 package 目录下执行 go mod init packageName 并且设置 Go 环境变量 GO111MODULE="on"

export GO111MODULE="on"

Go Package 的导入可以支持给包名起别名

package main

import (
   // 起别名
   ope "zulu/operator"
)

func main(){
   
   // 使用别名调用
   ope.Add(1, 2)
}

如果导入包不使用,为了避免报错可以用 _ 命名

package main

import (
    // 别名为 _
    _"zulu/operator"
)

func main(){
   
}