1.1 安装 Go

Windows 安装(你的环境)

  1. 打开官方下载页:https://go.dev/dl/
  2. 下载 go<版本>.windows-amd64.msi(例如 go1.26.x.windows-amd64.msi
  3. 双击安装,一路下一步即可。默认安装路径是 C:\Program Files\Go
  4. 安装程序会自动把 Go 加入 PATH

验证安装

打开一个新的 cmd 窗口(重要:旧窗口不会刷新环境变量),运行:

go version

看到类似输出就成功了:

go version go1.26.0 windows/amd64

如果提示「'go' 不是内部或外部命令」,说明 PATH 没生效。重启电脑,或手动把 C:\Program Files\Go\bin 加到系统 PATH。


1.2 关键概念:GOROOT 与 GOPATH

装完 Go,你会遇到两个路径概念。现代 Go(1.11+)你基本不用管它们,但了解一下有好处:

概念 含义 类比
GOROOT Go 安装目录(含标准库源码) Node.js 安装目录
GOPATH 你的工作区,第三方包和编译产物放这里 全局 node_modules

注意:Go 1.11 引入 Go Modulesgo mod)之后,项目不再需要放在 GOPATH 下。你可以把项目放在任何位置(比如你的 D:\Project_Test\AIStudy\Go\)。这也是现代 Go 项目的标准做法。


1.3 配置国内代理(强烈推荐)

Go 下载第三方包默认从 proxy.golang.org 拉,国内访问慢甚至失败。装完后第一件事就是配置国内代理:

go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on

这两条命令会写入 Go 的环境配置文件(不是系统环境变量),对当前用户全局生效。

验证:go env GOPROXY 应输出 https://goproxy.cn,direct

如果您在设置之前启动了其它终端,需要重启一下那个终端才生效。


1.4 配置 VSCode

VSCode 是你已经在用的编辑器,对 Go 支持非常好。

安装 Go 扩展

  1. 打开 VSCode,进入扩展面板(Ctrl+Shift+X
  2. 搜索 Go,安装官方的 Go Team at Google 出品的扩展(图标是 Go gopher)

触发工具链安装

安装扩展后,一般几秒钟后,VSCode 会提示你安装 Go 的辅助工具(gopls 语言服务器、调试器等)。点「Install All」全部安装。如果没弹出提示,手动触发:按 Ctrl+Shift+P → 输入 Go: Install/Update Tools → 全选 → 确定。

⚠️ 这些工具会通过 go install 下载。如果你忘了配 1.3 的代理,这里会很慢或失败。装不上就回头配代理。

推荐配置

Ctrl+Shift+PPreferences: Open User Settings (JSON),加入以下 Go 相关配置:

{
  "go.formatTool": "goimports",
  "go.lintTool": "staticcheck",
  "go.useLanguageServer": true,
  "[go]": {
    "editor.formatOnSave": true,
    "editor.tabSize": 4,
    "editor.insertSpaces": false
  },
  "gopls": {
    "ui.semanticTokens": true
  }
}

几个要点:

  • formatOnSave: true —— 保存自动格式化。Go 社区对代码格式高度统一(用 gofmt),不争论花括号位置、缩进风格,编辑器统一处理。
  • insertSpaces: false —— Go 强制用 Tab 缩进(这是语言规范的一部分)。VSCode 装了 Go 扩展会自动处理,但显式设置更保险。

1.5 创建你的第一个 Go 程序

现在创建第一个项目。打开终端,切到你的学习目录:

cd /d D:\Project_Test\AIStudy\Go\code\ch01-hello

这个目录在教程里已经为你建好了,并放好了代码。我们下面一步步看是怎么来的。

第一步:初始化 module

任何一个 Go 项目都从一个 module 开始。在项目目录下运行:

# go mod init 表示初始化一个新的 Go 模块,会生成 go.mod 文件。相当于 npm init。
# github.com/yourname/ch01-hello 这是模块的名字,可以随意自己起名,比如:
# go mod init ch01-hello        # ← 纯名字,最简单
# go mod init study/ch01-hello
go mod init github.com/yourname/ch01-hello

这会生成一个 go.mod 文件,内容类似:

module github.com/yourname/ch01-hello

go 1.26.4

对比 Node.js

Node.js Go
初始化 npm init go mod init <模块名>
配置文件 package.json go.mod
锁文件 package-lock.json go.sum
装依赖 npm install xxx go get xxx@version

模块名github.com/yourname/ch01-hello)是这个项目的全局唯一标识,类似 npm 的包名。本地学习项目你可以随便起,但公开项目通常用 GitHub 仓库路径,这是 Go 的约定。

什么是 module?

module 就是 go.mod 文件定义的那个东西,它是一组 Go 包(package)的集合 + 版本号 + 依赖清单

关键判断标准:看有几个 go.mod 文件

一个 go.mod 文件 = 一个 module
场景 1:一个项目一个 module(主流,90% 的项目)

比如你的博客项目:

blog-api/
├── go.mod          ← 整个项目只有这一个 go.mod = 一个 module
├── main.go
└── internal/
    ├── handler/
    ├── service/
    └── repository/

所有子目录(handler、service 等)都是这个 module 下的不同 package,它们共享同一个 go.mod,共享同一套依赖。这是推荐做法

场景 2:一个项目多个 module(monorepo)

大公司常见,比如一个仓库里放多个独立的库:

my-monorepo/
├── go.work                ← 用 go.work 把多个 module 串起来(Go 1.18+)
├── module-a/
│   ├── go.mod             ← module A
│   └── main.go
├── module-b/
│   ├── go.mod             ← module B
│   └── main.go
└── shared-lib/
    ├── go.mod             ← module C
    └── utils.go

每个子目录有自己的 go.mod,是独立的 module,有独立的依赖版本go.work 文件让它们在本地开发时能互相引用。

典型例子:Go 官方很多大项目、Kubernetes、字节内部的 eino 生态都是 monorepo + 多 module。

什么是 package ?

这个我们后面会细讲,这里只是简单介绍一下:一个目录就是一个包,一个包里可以有多个go文件,每个go文件开头必须声明:package 包名同目录下的所有 .go 文件,package 名必须完全一样

注意:包名可以和目录名不同,但 Go 社区强烈约定包名 = 目录名

项目根目录下的go文件的包名

这个有多种情况,这里先只讲 根目录是「可执行程序入口」的情况:模块根目录下写可执行程序,包名必须是 main,并且必须有 func main()

// main.go
package main        必须是 main

import "fmt"

func main() {       必须有 main 函数!
    fmt.Println("Hello")
}

第二步:写代码

在目录下创建 main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, Go!")
}

(教程里已经放好了完整版本,你可以直接用。)

第三步:运行

go run main.go

输出:

Hello, Go!

🎉 你的第一个 Go 程序跑起来了!


1.6 程序骨架逐行解读

别小看这五行代码,它包含了 Go 程序的所有基础要素:

package main              // ① 声明这个文件属于 main 包

import "fmt"              // ② 导入标准库 fmt(格式化输出)

func main() {             // ③ main 函数是程序入口
    fmt.Println("Hello, Go!")  // ④ 调用 fmt 包的 Println 函数
}

package main —— 包声明

每个 .go 文件第一行必须是包声明。package main 是特殊的——它告诉 Go「这是一个可执行程序,而不是库」。

  • package main + func main() = 可执行程序(window下能编译成 .exe)
  • 其他 package xxx = 库,被别人 import 使用

对比:相当于 Node.js 里区分「应用入口」和「npm 包」。

import "fmt" —— 导入包

导入标准库的 fmt 包(format 缩写),用于格式化输入输出。

import "fmt"

// 多个包可以这样写:
import (
    "fmt"
    "os"
    "strings"
)

对比 Node.js

// Node.js
const fmt = require('fmt')      // ❌ 不存在,类比而已
const { println } = require('./utils')

// Go
import "fmt"                    // 标准
import "github.com/gin-gonic/gin"  // 第三方

⚠️ Go 和 Node.js 的关键差异Go 不允许导入未使用的包。如果你写了 import "fmt" 但没用 fmt编译直接报错。这是 Go 强制的简洁性。

func main() —— 入口函数

main 函数是程序入口,必须main 包里,必须没有参数和返回值。

对比:相当于 Node.js 脚本从上到下执行,但 Go 必须有显式的 main 函数。

fmt.Println(...) —— 函数调用

调用 fmt 包里的 Println 函数。

注意:Go 中函数名首字母大小写有特殊含义(1.9 节详讲):

  • Println 首字母大写 → 公开(导出),外部可以调用
  • println 首字母小写 → 私有,只在包内可用

fmt.Println 是大写 P,所以它是对外公开的。这是 Go 的可见性规则,非常重要,后面会反复遇到。


1.7 常用命令速查

学完这章,记住这几个命令就够了:

命令 作用 类比 nodejs
go mod init <name> 初始化项目 npm init
go run main.go 直接运行(不产出文件) node main.js
go build 编译成二进制 tscpkg
go build -o app.exe 编译并指定输出名
go install 编译并装到 GOPATH/bin npm install -g
go get <pkg> 下载依赖 npm install
go fmt 格式化代码 prettier
go vet 静态检查 eslint
go test 跑测试 npm test

试一下编译命令:

go build -o hello.exe main.go

会在当前目录生成 hello.exe(约 2MB)。直接运行:

hello.exe

这就是 Go 的杀手锏:编译出来的二进制自带所有依赖,不需要装 Go 也能跑。这点完胜 Node.js(需要装 Node + node_modules)。


1.8 Node.js ↔ Go 对照表

概念 Node.js Go 备注
包管理 npm go mod Go 没有中央注册中心,用 Git 仓库地址
初始化 npm init go mod init <name> Go 必须给模块名
依赖清单 package.json go.mod
锁文件 package-lock.json go.sum
装依赖 npm install xxx go get xxx
运行 node xxx.js go run xxx.go Go 不产生二进制
编译打包 webpack / pkg go build Go 产出单一可执行文件
入口 文件从上到下 func main() Go 必须有 main 函数
输出 console.log fmt.Println
格式化字符串 模板字符串 ` ` fmt.Sprintf("%s", x) Go 用 C 风格占位符

1.9 包与 import 快速了解

你可能一直在想:前面写的 package mainimport "fmt" 到底是什么?go mod init 又是干嘛的?这一节把这些最小够用的概念一次讲清楚,让你后续章节不再一头雾水。

⚠️ 这是「速成版」,只讲前 8 章会用到的最小集。第 09 章会系统讲 internal 包、项目结构、依赖管理等进阶内容

1.9.1 package —— 每个文件的「身份证」

你注意到了吗——每个 .go 文件第一行都是 package xxx。这行叫包声明,告诉 Go「这个文件属于哪个包」。

package main     // ← 这个文件属于 main 包

包是什么? 你可以把它理解成「一组相关代码的名字」。就像 JS 里一个文件夹是一个模块,Go 里一个目录就是一个包

1.9.2 package main 的特殊性

为什么我们的程序都写 package main?因为 main 是个特殊名字:

  • package main + func main() = 这是一个可执行程序(能编译成 .exe)
  • package xxx(其他名字)= 这是一个,给别人 import 用,不能直接运行
// main.go —— 可执行程序
package main          // ← main 是特殊包

func main() {         // ← 必须有 main 函数(程序入口)
    fmt.Println("hi")
}

对比 Node.js:Node.js 的入口是「从上到下执行文件」,Go 的入口是「找 package main 里的 func main」。

1.9.3 import —— 借用别人的代码

你写 import "fmt" 就是告诉 Go:「我要用 fmt 这个包」。fmt 是标准库(Go 自带)的格式化输出包。

import "fmt"           // 导入单个

import (               // 导入多个(推荐这种写法)
    "fmt"
    "os"
    "strings"
)

然后用的时候要加包名前缀

fmt.Println("hi")      // fmt 包的 Println 函数
os.Getwd()             // os 包的 Getwd 函数

⚠️ Go 不允许导入未使用的包——如果你写了 import "os" 但代码里没用 os,直接编译报错。这是 Go 强制的简洁性(不像 JS 那样可以 import 了不用)。

1.9.4 大小写决定「能不能被别人用」

这是 Go 最独特的设计:首字母大写的标识符能被别的包访问,小写的只能在本包内用

package mathutil

func Add(a, b int) int { ... }    // 大写 A:别的包能用 mathutil.Add
func helper() { ... }              // 小写 h:只有 mathutil 包内能用
package main
import "xxx/mathutil"

mathutil.Add(1, 2)     // ✅ 大写,能用
// mathutil.helper()   // ❌ 小写,访问不到

为什么用大小写而不是 public/private 关键字? Go 觉得这样更简洁——一眼就能看出某个东西是公开还是私有。这个规则后续会大量遇到,记住「大写=公开,小写=私有」就行。

1.9.5 go mod —— 项目的「身份证」

还记得 1.5 节你跑了 go mod init 吗?它生成了 go.mod 文件:

module github.com/yourname/ch01-hello

go 1.22

这是什么?

  • module 那行:这个项目的全局唯一名字(类似 npm 包名)
  • go 1.22:这个项目用的 Go 版本

类比 Node.js

  • go.modpackage.json(项目配置)
  • module 名 ≈ npm 包名
  • go mod initnpm init

后续装第三方库时,go.mod 会自动记录依赖(类似 package.json 的 dependencies)。

1.9.6 go run vs go build

命令 作用 类比 Node.js
go run main.go 直接运行(不生成文件) node main.js
go build 编译成二进制 (JS 没有直接对应,类似 webpack 打包)

go run 是开发时用的(快),go build 是发布时用的(生成独立的 .exe,拷到任何机器都能跑,不需要装 Go)。