fbpx

Khóa học Go lang cơ bản đầy đủ nhất 2023

Go lang là mã nguồn thông minh & có tốc độ cực nhanh tới từ Google. Cùng tìm hiểu những ghi chép & take note của Nam về mã nguồn này nhé. Hy vọng bài viết sẽ giúp ích được cho bạn

Go lang là mã nguồn thông minh & có tốc độ cực nhanh tới từ Google. Go Lang có một thư viện rất nhiều tài nguyên mà bạn có thể tham khảo tại godoc.org

Sử dụng lệnh go get “Tài nguyên” để load những dependencies về

Một Workspace Go lang cơ bản gồm những gì

Workspace của Go sẽ bao gồm 2 file chính

  • src bao gồm source của file
  • bin bao gồm các lệnh & hàm

Dưới đây là 1 ví dụ về 1 cấu trúc Go lang cơ bản

  • bin chứa binary of code 
  • src chứa code từ các project & nguồn khác nhau
  • pkg chứa khai báo các thành phần 

Tạo 1 folder với cấu trúc cơ bản kể trên. Để mở chương trình, project go_crash_course, ta đánh vào “code .” để hiển thị workspace đó trên Visual Studio Code

Với cấu trúc được liệt kê ở phía trên thì khi ta go get 1 chương trình tại Godoc, các folder cũng sẽ tuân theo cấu trúc cơ bản kể trên. 

Các chương trình sẽ nhảy vào phần src dưới cái tên của đơn vị phát triển(github…) rồi mới vào tới tên của từng chương trình. 

001 – Bắt đầu viết chương trình Hello trên Go lang

Đầu tiên, ta sẽ xây dựng main.go có thể coi như một file index trong html

File main.go sẽ có các lệnh như sau: 

package main

import “fmt”

func main() {

    fmt.Println(“Hello World”)

}

Trong đó, package main là câu mở đầu lúc nào cũng có để tạo tiền đề định nghĩa chức năng của main 

import “fmt” chương trình print lệnh trên log của Go 

Như vậy khi đã định nghĩa đủ main & chương trình fmt thì ta sử dụng lệnh

func main() {

fmt.Println(“Hello world”) // Sử dụng chương trình fmt để in ra dòng lệnh (Println) mang tên “Hello world”

}

Tiếp đó bạn sử dụng lệnh go install để xuất chương trình ra file bin 

002 – Thiết lập biến trong Golang 

Tiếp theo, ta xây dựng chương trình gọi các biến và in chúng ra. Sử dụng hàm Printf (Print format ) để xác định các kiểu dữ liệu cần thiết. 

Sử dụng var biến = “” tương tự javascript & nodejs. Bạn cũng có thể in ra một danh sách các biến được định nghĩa  trước. 

Biến cũng có thể được định nghĩa phía trên đầu của file main.go & chương trình vẫn sẽ hiểu & chạy được

  • Như hình, chương trình đã được đặt biến var name = ”Bad” lên đầu
  • Có thể sử dụng cách gọi biến nhanh name := “Brad”
  • Có thể sử dụng cấu trúc gọi gộp 2 biến lại và định nghĩa cùng 1 lúc

03 – Packages

Tạo 1 module mới & sử dụng hàm math để tính toán, lưu ý rằng: 

Hàm nào được import vào sẽ không xuất hiện nếu ta không sử dụng

Có thể sử dụng các module tự dựng sẵn, ví dụ như tạo 1 hàm reverse & khai báo chúng ra. 

package strutil

func Reverse(s string) string {

    runes := []rune(s)

    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {

        runes[i], runes[j] = runes[j], runes[i]

    }

    return string(runes)

}

Trong đó, hàm reverse được định nghĩa đối với đối tượng biến là string: đoạn text

04 – Hàm Function

Đối với các hàm function, Go lang có thể xử lý như sau

package main

import “fmt”

func greeting(name string) string {

    return “Hello " + name

}

func main() {

    fmt.Println(greeting(“Nam”))

}

Trong đó, fmt là chương trình giúp ta in lệnh ra console. Còn func greeting(name string) là định nghĩa chức năng greeting với biến là tên (string), còn với đó, kết quả cũng được gọi là string

Đối với các hàm có nhiều tham số, ta làm tương tự, có thể gộp hoặc định nghĩa nhiều biến

05 – Hàm Array Slice

Go lang giúp bạn trả về giá trị từ 1 mảng dữ liệu được định nghĩa sẵn hoặc tạo ra một slice(thanh trượt chứa những mảng) 

06 – Hàm Conditionals

package main

import “fmt”

func main() {

    x := 15

    y := 10

    if x < y {

        fmt.Printf("%d is less than %d\n”, x, y) //cách sử dụng hàm Prinf

    } else {

        fmt.Printf("%d is less than %d\n", y, x) //cách sử dụng hàm Prinf

    }

}

Hàm Conditionals giúp ta tạo các điều kiện tương tự như các ngôn ngữ lập trình khác. Cấu trúc fmt.Printf(“%d nhỏ hơn %d\n”, x, y) cho ta thấy khi gán 2 giá trị x đứng trước cho %d và y đứng sau cho %d\n ta nhận được kết quả như vậy. 

Hàm else if sử dụng tương tự PHP & Javascript

    // else if

    color := “red”

    if color == “red” {

        fmt.Println(“color is red”)

    } else if color == “blue” {

        fmt.Println(“color is blue”)

    } else {

        fmt.Println(“color is not blue or red “)

    }

Đối với hàm switch 

// switch

    switch color {

    case “red”:

        fmt.Println(“color is red”)

    case “blue”:

        fmt.Println(“color is blue”)

    default:

        fmt.Println(“color is Not red”)

    }

}

Trong đó default là các trường hợp còn lại

07 – Hàm loops

func main() {

    // Long method

    i := 1

    for i <= 10 {

        fmt.Println(i)

        //i = i + 1

        i++

    }

    // Short method

    for i := 1; i <= 10; i++ {

        fmt.Printf(“Number %d\n”, i)

    }

    // Fizzbuzz

    for i := 1; i <= 100; i++ {

        //Nếu i chia hết cho 15

        if i%15 == 0 {

            fmt.Println(“Fizzbuzz”)

        } else if i%3 == 0 {

            fmt.Println(“Fizz”)

        } else {

            fmt.Println(i)

        }

    }

}

Tính năng định nghĩa một biến i, nếu biến i chạm tới 1 giá trị nào đó thì cho i đó liên tục tăng dần lên 1 giá trị mỗi lần (i++)

Ví dụ thứ 3 giúp ta in ra các ký tự Fizz hoặc Fizzbuzz nếu kết quả trả về chia hết cho 15 hoặc chia hết cho 3

08 – Maps

package main

import “fmt”

func main() {

    // Define map

    emails := make(map[string]string)

    //Assign kv

    emails[“Bob”] = “[email protected]”

    emails[“Nam Bá”] = “[email protected]”

    fmt.Println(emails)

    fmt.Println(emails[“Bob”])

}

Map giúp ta tạo 1 data ánh xạ thành phần này với thành phần kia, ví dụ ở đây map nối 2 địa chỉ trong ngoặc vuông là tên còn kết quả trả về là địa chỉ email

  • Khi in 1 danh sách map của hàm emails ta sử dụng fmt.Println(emails)
  • Nếu muốn in một địa chỉ email tương ứng, ta sử dụng fmt.Println(emails[“Tên”])

    //Delete from map

    delete(emails, “Bob”)

    fmt.Println(emails)

Ta cũng có thể tiến hành xóa một thành phần ra khỏi maps 

Tương tự như vậy, ta cũng có thể gán 1 lúc nhiều giá trị ánh xạ sang nhiều biến trong 1 maps để tăng tốc quá trình triển khai

09 – Range

package main

import “fmt”

func main() {

    ids := []int{33, 76, 54, 23, 11, 2}

    // Loop through ids

    for i, id := range ids {

        fmt.Printf("%d - ID: %d\n”, i, id)

    }

    // Not using index

    for _, id := range ids {

        fmt.Printf(“ID: %d\n”, id)

    }

}

Lý giải một chút: 

Ban đầu ta gọi 1 mảng ids với các con số 

tiếp đó, ta gọi từng tham số i tương tự như loops nối với từng phần tử id nằm trong mảng đó (range ids)

fmt.Printf cho các con số %d – ID: %d\n đối với 2 tham số i & id

// Add ids together

    sum := 0

    for _, id := range ids {

        sum += id

    }

    fmt.Println(“Sum”, sum)

Đối với trường hợp này, go lang xử lý tương tự như js hay nodejs với việc tham số các id trên 1 dải ids

Đối với Range với Maps

// Range with map

    emails := map[string]string{“Bob”: “[email protected]”, “Nam”: “[email protected]”}

    for k, v := range emails {

        fmt.Printf("%s: %s\n”, k, v)

    }

    for k := range emails {

        fmt.Println(“Name " + k)

    }

Ta gọi key & value trong dãy emails, sử dụng cấu trúc Printf để tạo ra các biến pair với nhau 

10 – Pointers

package main

import “fmt”

func main() {

    a := 5

    b := &a

    fmt.Println(a, b)

    fmt.Printf("%T\n”, b)

}

Pointers gọi ra giá trị memory của giá trị a

Nếu ta sử dụng hàm Printf để in ra data type của go lang thì hàm này có giá trị *int

Lý do sử dụng pointers – Để cập nhật các bản ghi & thay đổi các giá trị

12 – Structs

Đây là 1 module khá quan trọng của go lang, vì nó giúp ta định nghĩa các trường & thực hiện các thao tác điều khiển, hiển thị dữ liệu

package main

import “fmt”

// Person struct define

type Person struct {

    firstName string

    lastName  string

    city      string

    gender    string

    age       int

}

func main() {

    // Init person using struct

    person1 := Person{firstName: “Nam”, lastName: “Nguyen”, city: “Boston”, gender: “m”, age: 25}

    fmt.Println(person1)

}

Như ở ví dụ này, ta định nghĩa 1 cấu trúc Person với các trường & kiểu dữ liệu tương ứng.  Tiếp đó, ta gọi một biến khai báo đầy đủ các thành phần cấu trúc này & tiến hành gọi chúng ra bằng Println

Ngoài ra, dữ liệu được định nghĩa trước đã sắp xếp theo thứ tự, chính vì vậy ta hoàn toàn có thể tạo ánh xạ dữ liệu từ biến theo thứ tự sắp xếp đã định. 

Ví dụ về việc sử dụng value receiver

package main

import (

    “fmt”

    “strconv”

)

// Person struct define

type Person struct {

    firstName string

    lastName  string

    city      string

    gender    string

    age       int

}

// Greeting method (value receiver)

func (p Person) greet() string {

    return “Hello, my name is” + p.firstName + " " + p.lastName + " and I am " + strconv.Itoa(p.age)

}

func main() {

    // Init person using struct

    person1 := Person{firstName: “Nam”, lastName: “Nguyen”, city: “Boston”, gender: “m”, age: 25}

    fmt.Println(person1)

    fmt.Println(person1.greet())

}

vì age có data type là int nên ta không thể đưa chúng vào hàm return, do vậy, ta cần import 1 hàm có sẵn là strconv(Convert kiểu dữ liệu thành string)

Cấu trúc convert 1 hàm là strconv.Itoa(p.age)

Như ta thấy ở hình, brad đã làm 2 trường hợp với 2 kiểu dữ liệu khác nhau

  • value receiver: đối với p Person(tương đương với this của js
  • pointer receiver: Nếu this của dữ liệu được lưu trữ có sinh nhật thì p.age tăng lên

Một ví dụ khác về pointer receiver

// getMarried (pointer receiver)

func (p *Person) getMarried(spouseLastName string) {

    if p.gender == “m” {

        return

    } else {

        p.lastName = spouseLastName

    }

}

Nếu giới tính là Nam thì trả về kết quả bình thường, còn nếu giới tính là nữ thì p.lastName sẽ được sửa theo giá trị của biến được pointer

person1.getMarried(“Williams”)

Trỏ tới person 1 func getMarried với tên biến spouseLastName là Williams

14 – Web – Quan trọng

package main

import (

    “fmt”

    “net/http”

)

func index(w http.ResponseWriter, r *http.Request) {

    fmt.Fprintf(w, “Hello World")

}

func about(w http.ResponseWriter, r *http.Request) {

    fmt.Fprintf(w, “Về Nam Digital")

}

func main() {

    http.HandleFunc("/”, index)

    http.HandleFunc("/about”, about)

    fmt.Println(“Server starting”)

    http.ListenAndServe(":3000", nil)

}

Cùng định nghĩa 1 chút:

Tại đây ta cũng thực hiện định tuyến ứng dụng với 2 gói import có sẵn là fmt & net/http

Trước hết, ta định nghĩa nội dung của 2 hàm index & about với cấu trúc w: write – viết dữ liệu, http.ResponseWriter & r: read yêu cầu đọc dữ liệu

Tiếp đó, ta thực hiện in ra trình duyệt thẻ như trên

Cuối cùng, ta định tuyến 2 route index & about và gắn cho chúng 2 function. Cuối cùng, server được triển khai trên cổng 3000 & 1 dòng console.log là server starting được in ra. 

Update 2023: Cách triển khai xây dựng 1 web tĩnh cùng Golang

Để xây dựng một web tĩnh, ta cần triển khai Web server sử dụng gói gorilla mux, gói này sẽ thực hiện các tác vụ

Triển khai Routing với Gorilla Mux

  • Định tuyến: tạo các Routes
  • Xây dựng Web Server lắng nghe cổng theo mong muốn của bạn

Dưới đây là một ví dụ về việc tạo các Routes

// Định nghĩa hàm hiển thị
	r := mux.NewRouter()
	r.HandleFunc("/", Index)

Trong đó, biến r chính là request (hiển thị yêu cầu xử lý), đối với routes “/” – thường mặc định là trang chủ thì ta truyền 1 hàm Index vào đó

Hàm Index có thể có dạng như sau:

func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Chào mừng đến với Nam Digital.vn")


	config.Tpl.ExecuteTemplate(w, "index.html", nil)
}

Trong đó hàm Index là một hàm có 2 tham số là w và r, tương đương với những chú giải ở phần 14 phía trên, hàm này sẽ đẩy ra Terminal một đoạn text là “Chào mừng đến với Nam Digital.vn” đồng thời xử lý một tính năng của hàm html/template nhằm load trang index.html và vì không truyền tham số gì nên ta để là nil

Một điều quan trọng không thể thiếu nữa là bạn cần thiết lập việc lắng nghe và xử lý các tệp tin ảnh, media để có thể chèn vào các file html dễ dàng

	// Tạo đường dẫn tới 2 thư mục assets và public để hiển thị dữ liệu của web, cũng như dữ liệu bài viết sau này
	r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets", http.FileServer(http.Dir("./assets"))))
	r.PathPrefix("/public/").Handler(http.StripPrefix("/public", http.FileServer(http.Dir("./public"))))

Một công đoạn cuối là bạn cần tạo ra việc lắng nghe ở cổng tương ứng khi chạy server

// Chạy server về cổng mong muốn
	srv := &http.Server{
		Handler: r,
		// Cổng này để chạy với Docker
		Addr: "0.0.0.0:80",
		// Tạo 1 khoảng trống giữa việc đọc và ghi
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}

	log.Fatal(srv.ListenAndServe())

Lưu ý ở trên là mình triển khai Docker nên mới để Addr theo hướng kia, bạn có thể đổi thành localhost hoặc 127.0.0.1(Tương ứng với localhost)

Triển khai hàm html/template để tải nội dung html

Dưới đây là một ví dụ của việc triển khai hàm html/template, web server sẽ lắng nghe và xử lý các file html ở vị trí tương ứng, từ đó đẩy ra những template nội dung phù hợp với những đường dẫn ta đã định tuyến ở phần trên

var Tpl *template.Template

func init() {
	// Đăng ký hàm TPL để GO đọc các file nằm trong thư mục templates
	Tpl = template.Must(template.New("").Funcs(template.FuncMap{
	}).ParseGlob("templates/*/*"))

}

Từ đây bạn đã xây dựng một biến Tpl để tái sử dụng trong dài hạn, từ đó tiếp tục sử dụng hàm Must trong gói html/template để lắng nghe những tệp tin nằm trong đường dẫn ./templates/*/*,

Học Wowchemy

Wowchemy là nền tảng build web nhanh chóng theo nền tảng golang, mà cụ thể ở đây là framework tên là “Hugo”

Đây là cấu trúc cơ bản của một Project Hugo với Wowchemy. 

Tạo một Section mới trong 1 trang cho trước

Để tạo 1 section mới, ta tạo ngay trong content / tên page / 

Giả sử bạn muốn tạo 1 section với tên là my-section.md, trước và sau mỗi section sẽ được đánh dấu bằng cách kí tự như “—” hay “+++”. Ta hãy cùng tìm hiểu các yếu tố này

Front Matter (—)

Front matter allows page-specific metadata and functionality to be included at the top of a Markdown file.

In the documentation and the example site, we will predominantly use YAML to format the front matter of content files and TOML to format the configuration files

YAML là gì?

YAML (YAML Ain’t Markup Language) là một chuẩn dữ liệu kiểu serialization dành cho tất cả các ngôn ngữ. Nó được sử dụng phổ biến để tạo ra các file config cho nhiều ứng dụng, VD: như Docker Compose.

TOML là gì?

Đây là loại file chứa các dữ liệu config trong hugo

Ví dụ bạn muốn tạo 1 Hero section

Định nghĩa hero section này trong /content/home/my-section.md

Tiếp đó bạn điền Frontmatter (—) trước và sau phần khai báo để xây dựng các meta data trước, 

Cuối cùng khi kết thúc thành phần khai báo, bạn có để điền vào phần body ngay dưới cặp thẻ (—)

Nếu bạn sử dụng yếu tố TOML (+++), cần chắc rằng những biến bạn tạo ra phải được định nghĩa trước trong phần Config 

Tạo 1 Widget mới

Widget có thể được tạo dễ dàng, ta sẽ tìm hiểu trong phần sau

Những Section đều có thể thêm các khối như Icon của Fontawesome 

Tùy chỉnh Css của từng Section

Để tùy chỉnh từng thành phần background của section, bạn chèn thuộc tính 

Điều chỉnh Background

Thành phần design và background có thể dễ dàng chỉnh sửa 

Style

Bạn cần định nghĩa style trong template.scss, tiếp đó, bạn mới có thể gọi tên class trong từng section

Pipeline là gì

There’s no formal definition of a pipeline in Go; it’s just one of many kinds of concurrent programs. Informally, a pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function.

Pipeline là một khái niệm không có định nghĩa chính thức, nhưng là một phần vô cùng quan trọng được sử dụng trong Go, là một trong những thành phần không thể thiếu trong ngôn ngữ đa luồng. Một cách đơn giản là pipeline là một series những trạng thái kết nối theo từng kênh, khi mà mỗi trạng thái này biểu đạt một biến được biến đổi theo những function khác nhau

Ví dụ tại template.html

{{ . | fdateMDY }}

Trong đó fdateMDY là 1 function được định nghĩa sẵn trong main.go, khi sử dụng html.Execute để xử lý dữ liệu tại template.html, pipeline fdateMDY sẽ được gán vào bối cảnh (context ” . ” )

Array

Nam là 1 Growth Hacker, Developer đam mê với sự nghiệp phát triển web