Golang是2009年由谷歌的三位大佬 创造的一门新语言。Goalng有很多优点。比如,作为静态类型语言,Golang的依赖少,易于部署。它还支持协程并发,支持垃圾回收,可跨平台编译。Golang兼具动态语言的强大表达能力和C的执行效率,被誉为21世纪的C语言。
最近在学习Golang,为了寻找配置教程、优秀的电子书以及难度曲线适中的入门实践项目,我颇费了些功夫。趁着记忆还新鲜,我想分享一下我的学习路径以及学习资料。本文涉及Golang的安装、配置、运行以及实践项目。希望通过阅读本文,初学者们可以更快地找到适合自己的学习方向。
下载安装
官网提供标准安装包,一路点击就能完成安装。
安装包的链接是https://golang.org/dl/,更多关于安装的信息见安装 Go.
环境配置
(一)在终端运行
在命令行运行程序几乎不需要配置。只要下载Golang,然后按官方建议,创建一个名为gocode的文件夹,直接在里面写代码就可以了。为了防止以后import其他包发生错误,建议把gocode文件夹放在GOPATH下。如何知道Golang的工作路径(GOPATH)在哪里呢?打开命令行,输入go env
,就能在输出里找到工作路径了(注意区别安装路径和工作路径,它们是两个不同的路径)。
下面是Golang在命令行运行时的一些常用命令:
Linux系统下的常用命令(点击展开)
新建目录
$ mkdir foldername
打开目录
$ cd foldername
返回上一级目录
$ cd ..
新建go文件
$ touch filename.go
删除go文件
$ rm filename.go
重命名go文件
$ mv filename1.go filename2.go
用vim打开go文件
$ vim filename.go
运行go程序
$ go run filename.go
生成可执行的二进制文件
$ go build filename.go
运行二进制文件
$ ./filename
注:用vim打开文件以后,需要按a键打开编辑模式。编辑结束后,按esc键退出编辑模式。如果需要保存后退出,输入:wq
。如果不保存直接退出,输入:q
。最后按回车执行。
(二)在VSCode上运行
关于如何在VSCode上配置Golang运行环境,这个Youtube视频讲得很细:How to Configure Visual Studio Code for Go?
鉴于国内可能打不开YouTube,我用文字介绍一下配置步骤:
- 在VSCode安装go插件
- 打开Command Palette,输入
Go Install/Update Tools
。会弹出一列单选框,点全选,然后点OK安装 - 再次打开Command Palette,输入
Shell Command: Install 'code' command in PATH
,按回车安装
这样就配置完成啦!
运行go程序时,在Command Palette输入View: Toggle Integrated Terminal
,即可在VSCode内打开命令行界面。在命令行界面输入go run filename.go
,即可运行go程序。使用VSCode快捷键,可以更快地完成上述步骤,在此就不多作介绍了。
学习路径
(一)语言基础
Golang是一门简捷的语言,遵循敏捷开发(Agile Development)的原则,语言本身并不难。而且Golang官网提供了相当丰富的教程和代码示例,因此入门还是相当容易的。
对于初学者,建议先看官方的入门教程,再跑跑Go by Example上的Demo熟悉一下语言特性,就差不多了。
(二)入门书籍
入门书籍我推荐build-web-application-with-golang。这本书对概念的讲解细致到位,示例代码丰富,学习曲线也很平滑,适合初学者学习理解。在开始项目实践之前,可以先快速地浏览一下这本书。建议读一读2.2 Go基础中的代码,对深入了解Go语言还是挺有帮助的。
(三)项目实践
项目实践的最终目标是制作一个属于自己的Web application。为了实现这个目标,需要循序渐进。所以我们先用Go搭建我们的第一个静态网页,然后再尝试搭建动态网页。
(1)用Go搭建静态网页
搭建静态网页,我参考的是这个教程:Serving Static Sites with Go [PDF]。
按教程做完之后,我打算把之前在JavaScript初探中写的网页移植到这里验证一下。
详细代码(点击展开)
首先打开命令行,为项目创建目录。
$ mkdir to-do-list
$ cd to-do-list
除了在app.go中放置我们的代码,我们还需要创建static文件夹放置网页内容,templates文件夹放置模版。
$ touch app.go
$ mkdir -p static/stylesheets
$ touch static/example.html static/stylesheets/style.css
$ mkdir -p static/javascript
$ touch static/example.html static/javascript/main.js
$ mkdir templates
$ touch templates/layout.html templates/example.html
File: app.go
package main
import (
"html/template"
"log"
"net/http"
"os"
"path/filepath"
)
func main() {
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
http.HandleFunc("/", serveTemplate)
log.Println("\nWeb Server is available at http://localhost:3000/example.html")
http.ListenAndServe(":3000", nil)
}
func serveTemplate(w http.ResponseWriter, r *http.Request) {
lp := filepath.Join("templates", "layout.html")
fp := filepath.Join("templates", filepath.Clean(r.URL.Path))
// Return a 404 if the template doesn't exist
info, err := os.Stat(fp)
if err != nil {
if os.IsNotExist(err) {
http.NotFound(w, r)
return
}
}
// Return a 404 if the request is for a directory
if info.IsDir() {
http.NotFound(w, r)
return
}
tmpl, err := template.ParseFiles(lp, fp)
if err != nil {
// Log the detailed error
log.Println(err.Error())
// Return a generic "Internal Server Error" message
http.Error(w, http.StatusText(500), 500)
return
}
if err := tmpl.ExecuteTemplate(w, "layout", nil); err != nil {
log.Println(err.Error())
http.Error(w, http.StatusText(500), 500)
}
}
File: static/stylesheets/style.css
body {
font-family: "Source Han Sans", "San Francisco", "PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", sans-serif;
font-size: 14px;
color: #333;
}
a {
text-decoration: none;
color: #f66;
transition-property: color;
transition-duration: .2s;
transition-timing-function: ease-in-out;
}
a:hover {
color: #333;
}
p {
margin: 1em 0;
}
p:empty {
height: 1.5em;
}
main {
margin: 0 5em;
max-width: 60em;
}
h1 {
margin: 1em 0;
font-size: 24px;
font-weight: 300;
}
textarea, article {
width: 100%;
height: 12em;
margin: 2em 0;
padding: .5em;
overflow-y: scroll;
border: 1px solid #999;
border-radius: 2px;
line-height: 1.5;
text-align: justify;
text-justify: inter-ideograph;
}
textarea {
height: 9em;
font-size: 14px;
font-family: "Source Han Sans", "San Francisco", "PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", sans-serif;
color: #333;
}
article {
cursor: pointer;
}
button {
background: #fff;
border: 1px solid #ccc;
border-radius: 2px;
line-height: 1;
padding: .5em;
color: #666;
transition-property: color, background, border;
transition-duration: .2s;
transition-timing-function: ease-in-out;
}
button[id] {
border: 1px solid #999;
color: #333;
cursor: pointer;
}
button[id]:hover {
background: #666;
border-color: #666;
color: #fff;
}
::selection {
color: #fff;
background: #666;
}
::-moz-selection {
color: #fff;
background: #666;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
text-align: center;
}
th, td {
padding: 5px;
}
th {
text-align: left;
}
File: static/javascript/main.js
"use strict";
var Items = {
name: "",
isCheck: "false",
isDelete: "false"
};
var objArray = [];
var subArray = [];
var submit_flag = true;
var delete_flag = true;
function addElement() {
submit_flag = true;
var item = Object.create(Items);
var v = document.getElementById("myTextarea").value;
item.name = v
objArray.push(item);
var body = '<table><tr><th>#</th><th>Name</th></tr>'
for (var i = 0; i < objArray.length; i++) {
var index = i+1;
body += '<tr>';
body += '<td>' + index.toString(10) + '</td>';
//body += '<td>' + '<input type="checkbox" id=\"cb' + i.toString(10) + '\" onclick="isChecked(this);">' + '</td>';
body += '<td>' + objArray[i].name + '</td>';
//body += '<td>' + objArray[i].isDelete + '</td>';
//body += '<td>' + '<input type="button" id=\"btn' + i.toString(10) + '\" onclick="isDeleted(this);">' + '</td>';
body += '</tr>';
}
body += '</table></body></html>';
document.getElementById("object_list").innerHTML = body;
document.getElementById("myTextarea").value = '';
}
function submit() {
if (submit_flag == true) {
submit_flag = false;
subArray = objArray;
objArray = [];
var body = '<table><tr><th>#</th><th>Check Box</th><th>Name</th><th>Delete</th></tr>'
for (var i = 0; i < subArray.length; i++) {
var index = i+1;
body += '<tr>';
body += '<td>' + index.toString(10) + '</td>';
body += '<td>' + '<input type=\"checkbox\" id=\"cb' + i.toString(10) + '\" onclick=\"isChecked(this);\">' + '</td>';
body += '<td>' + subArray[i].name + '</td>';
body += '<td>' + '<input type=\"button\" id=\"btn' + i.toString(10) + '\" onclick=\"isDeleted(this);\">' + '</td>';
body += '</tr>';
}
body += '</table></body></html>';
document.getElementById("object_list").innerHTML = body;
}
}
function finalSheet() {
var myDate=new Date();
var final_sheet = '<html><head></head><style>html{background-position:center top;background-repeat:no-repeat;width:800px;margin:auto;}table, th, td {border: 1px solid black;border-collapse: collapse;}th, td {padding: 5px;}th {text-align: center;}</style><body><h1>计划完成统计表</h1><h4>' + myDate + '</h2>'
var body = '<table><tr><th>#</th><th>Name</th><th>Done</th></tr>'
var i = 0;
var index = 1;
var showtext = "";
while (i < subArray.length) {
if (subArray[i].isDelete != "true" ) {
if (subArray[i].isCheck == "true") {
showtext = "√";
}else {
showtext = "×";
}
body += '<tr>';
body += '<td>' + index.toString(10) + '</td>';
body += '<td>' + subArray[i].name + '</td>';
body += '<td>' + showtext + '</td>';
body += '</tr>';
index++;
}
i++;
}
body += '</table></body></html>';
document.getElementById("object_list").innerHTML = body;
final_sheet += body
download('计划完成统计表.html', final_sheet)
}
function handleEnter(event) {
if (event.keyCode == 13 && !event.shiftKey) {
addElement();
}
}
function clearText() {
document.getElementById("myTextarea").value = '';
}
function clearContents(element) {
element.value = '';
}
function isChecked(element) {
var id_str = element.id
id_str = id_str.replace("cb","");
var id_num = parseInt(id_str)
if (element.checked == true){
subArray[id_num].isCheck = "true";
} else {
subArray[id_num].isCheck = "false";
}
}
function isDeleted(element) {
var id_str = element.id
id_str = id_str.replace("btn","");
var id_num = parseInt(id_str)
if (delete_flag == true) {
delete_flag = false;
subArray[id_num].isDelete = "true";
element.value="×"
} else {
delete_flag = true;
subArray[id_num].isDelete = "false";
element.value=""
}
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
File: templates/layout.html
{{define "layout"}}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{template "title"}}</title>
<link rel="stylesheet" href="/static/stylesheets/style.css">
<script src="/static/javascript/main.js"></script>
</head>
<body>
{{template "body"}}
</body>
</html>
{{end}}
File: templates/example.html
{{define "title"}}To Do List{{end}}
{{define "body"}}
<main>
<h1>计划表生成工具</h1>
<p>© 2019 Chang Luo | <a href="https://luochang212.github.io">Home</a></p>
<hr>
<p>这是俺写的小工具,什么?居然被你发现了 O_O !!!</p>
<textarea id="myTextarea" onkeydown="handleEnter(event)" onfocus="clearContents(this);" style="width:32em">使用说明:
在此框中输入你的计划,点击“添加计划”按钮,将你的计划添加至计划列表。接着点击“生成表格”按钮,通过勾选生成的表格中的复选框,告诉后台你的完成情况。最后点击“下载表格”按钮,下载计划完成统计表。</textarea><br>
<button type="button" onclick="addElement()">添加计划</button>
     
→
     
<button type="button" onclick="submit()">生成表格</button>
     
→
     
<button type="button" onclick="finalSheet()">下载表格</button>
               
<button id="btn_clear" type="button" onclick="clearText()">清空</button>
<br><br>
<p id="object_list"></p>
</main>
{{end}}
(2)用Go搭建动态网页
搭建动态网页,我参考了:
附录:资源列表
为什么学习Go语言:
Golang在VSCode上的配置:How to Configure Visual Studio Code for Go?。
通过例子学习Go语言的语法和特性,详见Go by Example。
一本讲解Go语言基础以及Web Application实现的电子书 build-web-application-with-golang。
第一个实践项目,用Golang搭建一个静态网站 Serving Static Sites with Go [PDF]。
第二个实践项目,用Golang作后端,React作前端,搭建一个动态网站。