Go 中的珍珠奶茶入门
在命令行里操作很酷,能让你感觉自己像黑客电影里的英雄。但它也可能让人觉得老派,单调的文本像雪崩一样铺天盖地;或者让人望而生畏,各种命令行参数、美元符号前缀,以及各种可能在毫无预警的情况下破坏系统的方法,都让人感到不知所措。
但命令行并非使用终端的唯一方式,还有终端用户界面 ( TUI) ,它能让你的程序使用起来更加友好。我非常喜欢 TUI,因为它既有新意又保留了传统特色。最近,Go 语言中的Bubble Tea库以及Charm Bracelet 的所有其他工具都因其简化了 TUI 的创建过程而备受关注。
目前为止,我发现珍珠奶茶最突出的优点有:
- 它采用与浏览器 UI 框架共享的Elm 架构,因此如果您已经使用过 React、Vue 或 Elm,会感觉很熟悉。
- Elm架构不仅对现代前端开发人员来说耳熟能详,而且它也是组织UI代码的绝佳方式,因此有利于以易于管理的方式构建应用程序,并逐步扩展其逻辑。
- 由于是用 Go 语言编写的,其一致的语法有利于通过阅读他人的代码来学习。
在本系列教程中,我将从零开始构建一个基础的 TUI 应用,用于记录你每天学习的代码内容,尤其适合参加 #100Devs 或 #100DaysOfCode 等项目的人。撰写本文时,该应用尚未完成,因此每篇教程都将着重讲解特定的概念。大致的教程路线图如下:
- 🐣 编写一个简单的 Hello World 应用,看看它的架构是如何运作的
- 📝 我们正在打造第一个真正的珍珠奶茶组件——菜单
- ✨ 使用类似 CSS 的Lipgloss库,通过一些样式设计,让我们的菜单看起来更酷炫。
- 🚇 为我们的应用添加路由功能,以便显示不同的页面
- 🫧 使用其他人制作的珍珠奶茶组件,并使用Bubbles库
- 📝 将签到信息保存到 JSON 文件
所以,准备好你的终端机和一根珍珠奶茶专用吸管,因为事不宜迟,让我们一起畅享珍珠奶茶吧!
🚧 编写我们的第一个基础应用程序
首先,我们将用 Bubble Tea 创建一个“hello world”应用程序,您可以通过按 Ctrl+C 退出,该应用程序还将向我们介绍 Bubble Tea 应用程序的各个部分。
首先,在名为“code-journal”的新目录中运行:
go mod init
go get github.com/charmbracelet/bubbletea
然后,创建一个名为 `<filename>` 的文件app.go,并添加以下代码:
package main
import (
tea "github.com/charmbracelet/bubbletea"
)
func main() {
p := tea.NewProgram(
newSimplePage("This app is under construction"),
)
if err := p.Start(); err != nil {
panic(err)
}
}
接下来,我们创建另一个文件,simple_page.go其中包含我们的第一个用户界面,这是一个只显示一些文本的简单页面:
go
package main
import (
"fmt"
"strings"
tea "github.com/charmbracelet/bubbletea"
)
// MODEL DATA
type simplePage struct { text string }
func newSimplePage(text string) simplePage {
return simplePage{text: text}
}
func (s simplePage) Init() tea.Cmd { return nil }
// VIEW
func (s simplePage) View() string {
textLen := len(s.text)
topAndBottomBar := strings.Repeat("*", textLen + 4)
return fmt.Sprintf(
"%s\n* %s *\n%s\n\nPress Ctrl+C to exit",
topAndBottomBar, s.text, topAndBottomBar,
)
}
// UPDATE
func (s simplePage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.(type) {
case tea.KeyMsg:
switch msg.(tea.KeyMsg).String() {
case "ctrl+c":
return s, tea.Quit
}
}
return s, nil
}
在分析代码之前,我们先运行一下看看它的作用。在终端中运行:
go build
./code-journal
你应该会看到类似这样的内容:
太棒了!你的第一个奶茶应用已经运行起来了。现在让我们仔细看看代码。
🧋 模型是珍珠奶茶的主要界面
主函数通过使用该模型创建一个新程序来启动程序simplePage。
func main() {
p := tea.NewProgram(
newSimplePage("This app is under construction"),
)
if err := p.Start(); err != nil {
panic(err)
}
}
我们称之为tea.NewProgram,其签名是:
func NewProgram(initialModel Model) *Program
然后调用该程序的 Start 方法即可启动我们的应用程序。但是,这是什么意思呢initialModel?
Model这是珍珠奶茶的主要界面。它有三种方法:
type Model interface {
Init() Cmd
Update(msg Msg) (Model, Cmd)
View() string
}
该Init方法在应用程序启动时调用,并返回一个对象tea.Cmd。对象Cmd大致包含“幕后发生的事情”,例如数据加载或时间流逝。但在本教程中,我们没有任何后台操作,因此我们的init方法仅返回一个对象nil。
func (s simplePage) Init() tea.Cmd { return nil }
接下来,我们来看View方法。Bubble Tea 的一个很棒的抽象概念是,你的整个用户界面显示都是一个字符串!而View这里就是你创建这个字符串的地方。
func (s simplePage) View() string {
textLen := len(s.text)
topAndBottomBar := strings.Repeat("*", textLen + 4)
return fmt.Sprintf(
"%s\n* %s *\n%s\n\nPress Ctrl+C to exit",
topAndBottomBar, s.text, topAndBottomBar,
)
}
因此,我们将文本放在simplePage一个由星号组成的框中,并在底部添加一条消息:“按 Ctrl+C 退出”。
但是,如果我们的应用程序无法处理用户输入,我们就无法退出,所以这就是我们的Update方法发挥作用的地方。
func (s simplePage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg.(type) {
case tea.KeyMsg:
switch msg.(tea.KeyMsg).String() {
case "ctrl+c":
return s, tea.Quit
}
}
return s, nil
}
该Update方法接收一个参数tea.Msg,并返回一个新的tea.Model参数,有时还会返回一个值tea.Cmd(例如,如果某个操作导致检索到一些数据或计时器响起)。
Atea.Msg的类型签名是
type Msg interface {}
所以它可以是任何类型的事件,并且可以携带任意数量的数据。如果你做过前端开发,它有点像 JavaScript 中的浏览器事件;例如,定时器事件不携带任何数据,点击事件会告诉你点击了什么,等等。
我们正在处理的消息类型是tea.KeyMsg,它代表键盘输入。我们正在检查用户是否按下了 Ctrl+C,如果是,则返回该tea.Quit命令,该命令类型为,tea.Cmd并指示 Bubble Tea 退出应用程序。
对于其他类型的输入,我们什么也不做,直接返回模型原样。但如果我们需要进行一些操作,比如用户界面导航,我们会修改模型中的一些字段,然后再返回,从而更新用户界面。接下来,我们将在下一个教程中创建一个菜单组件,届时就会用到这些内容!
文章来源:https://dev.to/andyhaskell/intro-to-bubble-tea-in-go-21lg
