1 2 3

Golang 学习笔记

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,我用文字介绍一下配置步骤:

  1. 在VSCode安装go插件
  2. 打开Command Palette,输入Go Install/Update Tools。会弹出一列单选框,点全选,然后点OK安装
  3. 再次打开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> 
	&nbsp &nbsp &nbsp
	→
	&nbsp &nbsp &nbsp
	<button type="button" onclick="submit()">生成表格</button>
	&nbsp &nbsp &nbsp 
	→
	&nbsp &nbsp &nbsp 
	<button type="button" onclick="finalSheet()">下载表格</button>
	&nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp
	<button id="btn_clear" type="button" onclick="clearText()">清空</button>
	<br><br>
	<p id="object_list"></p>

</main>
{{end}}

(2)用Go搭建动态网页

搭建动态网页,我参考了:

附录:资源列表

  1. 为什么学习Go语言:

  2. Golang在VSCode上的配置:How to Configure Visual Studio Code for Go?

  3. Go的安装、运行、设计理念等基础知识,详见官方文档入门教程

  4. 通过例子学习Go语言的语法和特性,详见Go by Example

  5. 一本讲解Go语言基础以及Web Application实现的电子书 build-web-application-with-golang

  6. 第一个实践项目,用Golang搭建一个静态网站 Serving Static Sites with Go [PDF]

  7. 第二个实践项目,用Golang作后端,React作前端,搭建一个动态网站。