大家好,我是沙漠盡頭的狼。

站长用Go写的这版博客前台代码比较原生,既没用什么框架,本来想着后面重构后再详细写写分享的,今天群友提了,就简单写写吧。
在线访问地址:https://go.dotnet9.com
首頁截圖:

本文寫的很簡單,還是來個目錄:
- go基礎學習
- go搭建博客參考
- go版本博客源碼
- 後面計劃
1. go基礎學習
- Go中文文档:https://go.p2hp.com/doc/
- go web編程:q群(771992300)有pdf文件分享
上面的網址和pdf,適合打基礎、鞏固go的基礎語法。其中pdf文件應該不是京東售賣的《go web編程》([新加坡] 鄭兆雄)一書,但也非常不錯,講解了不少web基礎知識,應該是網上一位大佬的總結,值得一讀,下面是該pdf目錄:

上面的基礎資料建議未接觸go的同學閱讀,基礎很重要。
2. go搭建博客參考
現重構的博客前台模板和go版本學習資料出處:
B站Up主【码神之路】B站首页:https://space.bilibili.com/473844125
教程標題:【碼神之路】原生go語言博客實戰教程,練手級項目實戰教程,未使用任何框架,通俗易懂,十年大廠程式設計師講解
視頻目錄如下:

視頻配套的博文截圖:

視頻適合學習、動手實踐,up主配套的博文則適合輔助檢查代碼是否正確,博客配套的前端模板在up主群里有分享(群號【718840650】),站長代碼倉庫也有上傳,但是有部分與up主改動不同,部分個性化修改如下:
- 站長未添加前台文章編輯:dotnet9網站有後台文章管理功能,功能重合了。
- orm選擇不同:站長選擇的gorm。
- 文章搜索站長直接調用的dotnet9網站後端接口,未在go中再寫接口實現:web api與前台職責分明,也為了其他客戶端接口共用,比如razor pages博客前台也使用了相同的文章搜索接口。
- 其他。
3. go版本博客源碼
如B站Up主【码神之路】视频教程标题所说“原生Go语言博客实战教程,练手级项目实战教程,未使用任何框架,通俗易懂”,重点是原生,站长实践后发现Up主的路由相关写法与 ASP.NET Core 的Minimal APIs(最小API)相像,当然前者主要是写Web(MVC),后者是写Web API,实践中与自己熟悉的技术比较学习能加深理解,下面对Go版博客源码进行部分简单介绍。
3.1入口
main.go为程序入口文件,相当于 ASP.NET Core 的Program.cs文件,文件名可以修改,只要代码第一行为package main。
全部代碼如下:
package main
import (
"log"
"net/http"
"dotnet9.com/goweb/common"
"dotnet9.com/goweb/router"
)
func init() {
// 模板加载:html文件,比如首页文章列表、分类文章列表、页头、页尾等
common.LoadTemplate()
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
router.Router()
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
服务绑定IP与端口是写死的,可写到配置文件,你就说和如下 ASP.NET Core 最小API像不像吧:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
router.Router()是简单封装的路由(封装如下),和上面最小APIapp.MapGet("/", () => "Hello World!");像几分,能给到8分不?:
go的簡單路由封裝:router.go
package router
import (
"net/http"
"dotnet9.com/goweb/views"
"dotnet9.com/goweb/api"
)
func Router() {
http.HandleFunc("/", views.HTML.Index)
http.HandleFunc("/c/", views.HTML.Category)
http.HandleFunc("/p/", views.HTML.Detail)
http.HandleFunc("/login", views.HTML.Login)
http.HandleFunc("/api/v1/post", api.API.SaveAndUpdatePost)
http.Handle("/resource/", http.StripPrefix("/resource/", http.FileServer(http.Dir("public/resource/"))))
}
路由首頁代碼如下:
./views/index.go
package views
import (
"errors"
"log"
"net/http"
"strconv"
"dotnet9.com/goweb/common"
"dotnet9.com/goweb/service"
)
func (*HTMLApi) Index(w http.ResponseWriter, r *http.Request) {
// 这里是业务代码,读取文章列表,绑定模板
}
3.n 模板綁定
中間跳過orm等使用,說說模板綁定:
這是go的文章列表模板:./ template/layout/post-list.html
{{define "post-list"}}
<ul class="post-box">
{{range $index, $elem := .Posts}}
<li class="post-label">
<a href="/p/{{$elem.Slug}}"><h2>{{$elem.Title}}</h2></a>
<p>{{$elem.Description}}</p>
<div class="post-action">
<span>
<i class="iconfont icon-yonghu"></i>
{{$elem.Original}}
</span>
<span>
<i class="iconfont icon-wenjianjia"></i>
{{range $catIndex, $cat := $elem.Categories}}
<a href="/c/{{$cat.Slug}}">{{$cat.Name}}</a>
{{end}}
</span>
<span>
<i class="iconfont icon-riqi"></i>
{{$elem.CreationTime}}
</span>
<span>
<i class="iconfont icon-yanjing"></i>
{{$elem.ViewCount}}
</span>
</div>
</li>
{{end}}
</ul>
{{end}}
对比这是Razor Pages文章列表模板绑定:
@page
@inject IOptionsSnapshot<SiteOptions> SiteOptions
@using Dotnet9.Web.ViewModel.Commons
@model IndexModel
@{
ViewData["Title"] = "首页";
}
<ul class="post-box">
@if (Model.BlogPosts?.Any() == false)
{
<span>没有数据哦</span>
}
else
{
@foreach (var blogPost in Model.BlogPosts!)
{
<li class="post-label">
<a href="@blogPost.CreationTime.ToString("/yyyy/dd")/@blogPost.Slug">
<h2>@blogPost.Title</h2>
</a>
<p>@blogPost.Description</p>
<div class="post-action">
<span>
<i class="iconfont icon-yonghu"></i>
@(blogPost.Original.IsNullOrWhiteSpace() ? SiteOptions.Value.Owner : blogPost.Original)
</span>
<span>
<i class="iconfont icon-wenjianjia"></i>
@foreach (var cat in blogPost.Categories)
{
<a href="/cat/@cat.Slug" title="@cat.Description">@cat.Name</a>
}
</span>
<span>
<i class="iconfont icon-riqi"></i>
@blogPost.CreationTime.ToString("yyyy-MM-dd")
</span>
<span>
<i class="iconfont icon-yanjing"></i>
@blogPost.ViewCount
</span>
</div>
</li>
}
}
</ul>
@await Html.PartialAsync("_PaginationPartial", new PaginationModel(Model.Current, Model.Pages, Model.Total, Model.PageSize, Model.PageCount))
代碼看個大概:
- Go模板绑定使用
{{.对象字段}},注意前面的点 - Razor语法绑定使用
@Model.对象
語言都是相似的,熟悉一個技術棧,其他技術棧可以觸類旁通。
3.n+1評論
评论对接的第三接口Valine,后面考虑自己开发:
valine - 一款快速、簡潔且高效的無後端評論系統。
特性
- 快速
- 安全
- Emoji 😉
- 無後端實現
- markdown 全語法支持
- 輕量易用
- 文章閱讀量統計 v1.2.0+
網站使用截圖:

valine留言後台管理:

4. 後面計劃
學習go,或者一門新技術,不能光是學,需要找機會用起來。嘗試使用go做一個自己感興趣的項目,比如開發一個簡單的博客前台,相信自己會學習的更有動力,起初可以嘗試原生開發,即不使用框架,對了解一門語言的基礎語法很有益處。
当想快速进行业务开发、迭代时,可以考虑框架了,站长最近有关注 goframe:
goframe是一款模塊化、高性能、企業級的go基礎開發框架。goframe是一款通用性的基礎開發框架,是golang標準庫的一個增強擴展級,包含通用核心的基礎開發組件,優點是實戰化、模塊化、文檔全面、模塊豐富、易用性高、通用性強、面向團隊。如果您想使用golang開發一個業務型項目,無論是小型還是中大型項目,goframe是您的不二之選。如果您想開發一個golang組件庫,goframe提供開箱即用、豐富強大的基礎組件庫也能助您的工作事半功倍。如果您是團隊leader,goframe豐富的資料文檔、詳盡的代碼注釋、活躍的社區成員將會極大降低您的指導成本,支持團隊快速接入、語言轉型與能力提升。
goframe官方文檔:https://goframe.org/pages/viewpage.action? pageid=1114119

后面就用goframe重构这版go博客吧,当然前提是先把Razor Pages版本博客开发完善了,包括后台...
水文結束(花了2小時),只要肯花時間學,沒什麼學不會的,不服就一個字:整。