🍵 一杯代码冲泡的珍珠奶茶:Go 语言 Bubble Tea 初探

“在命令行里工作很酷,会让你感觉自己像黑客电影里的英雄。”

此言不虚!但是,面对单调乏味的文字瀑布,或者令人望而生畏的命令行参数、美元符号前缀,以及各种莫名其妙的错误信息,我们也难免会感到力不从心。

好在,终端世界里除了冰冷的命令行,还有生机勃勃的 TUI(终端用户界面)。TUI 就像一杯珍珠奶茶,在复古的外表下,蕴藏着现代化的体验。而在 Go 语言的世界里, Bubble Tea 库的出现,让制作 TUI 应用变得像泡一杯奶茶一样简单。

🤔 为什么选择 Bubble Tea?

想象一下,你是一位经验丰富的茶饮师,想要调制一杯独一无二的珍珠奶茶。这时,你会选择什么样的工具呢?

Bubble Tea 就如同一个功能强大的工具箱,它提供了以下几个不可抗拒的优势:

  • 熟悉的 Elm 架构: 如果你已经习惯了 React、Vue 或 Elm 等前端框架,那么 Bubble Tea 的 Elm 架构对你来说一定不会陌生。这种架构就像奶茶的配方,清晰易懂,让你轻松上手。
  • 模块化的代码组织: Elm 架构不仅易于理解,而且非常适合构建模块化的 UI 代码。你可以从一个简单的应用开始,逐步添加功能,就像在奶茶中加入珍珠、椰果一样,让你的应用逐渐丰满起来。
  • 简洁易懂的 Go 语言: Go 语言以其简洁一致的语法著称,这使得阅读和理解他人的代码变得轻而易举。就像品尝一杯精心调制的奶茶,你能清晰地感受到每一种成分的味道。

🚀 从 Hello World 开始

俗话说,万事开头难。让我们从一个简单的 “Hello World” 应用开始,体验一下 Bubble Tea 的魅力吧!

首先,创建一个名为 code-journal 的目录,并在终端中运行以下命令:

go mod init
go get github.com/charmbracelet/bubbletea

然后,创建一个名为 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 的文件,其中包含我们的第一个 UI,一个只显示一些文本的简单页面:

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

你将会看到如下输出:

************************
* This app is under construction *
************************

Press Ctrl+C to exit

恭喜你!你已经成功运行了你的第一个 Bubble Tea 应用。

🔬 剖析代码

现在,让我们像品尝奶茶一样,细细品味一下这段代码。

🧋 Model:Bubble Tea 的核心

main 函数通过创建一个新的 simplePage 模型来启动程序。

p := tea.NewProgram(
    newSimplePage(
        "This app is under construction",
    ),
)

我们调用了 tea.NewProgram 函数,它的函数签名如下:

func NewProgram(initialModel Model) *Program

然后调用该程序的 Start 方法来启动我们的应用程序。但是 initialModel 是什么呢?

ModelBubble Tea 的核心接口,它定义了三个方法:

type Model interface {
    Init() Cmd
    Update(msg Msg) (Model, Cmd)
    View() string
}
  • Init() 方法在应用程序启动时被调用,并返回一个 tea.CmdCmd 可以理解为 “幕后发生的事情”,例如加载数据或计时器。在本教程中,我们没有任何后台操作,因此 Init 方法只返回 nil
func (s simplePage) Init() tea.Cmd {
    return nil
}
  • View() 方法是 Bubble Tea 中一个很酷的抽象,它将整个 UI 的显示都表示为一个字符串!在 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 的文本放在一个由星号组成的盒子里,并在底部显示一条消息 “Press Ctrl+C to exit”。

  • 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(例如,如果某个操作导致检索数据或计时器启动)。

tea.Msg 的类型签名如下:

type Msg interface{}

所以它可以是任何类型,并携带你需要的数据。它有点像 JavaScript 中的浏览器事件;计时器事件不携带任何数据,点击事件会告诉你点击了什么,等等。

我们正在处理的消息类型是 tea.KeyMsg,它表示键盘输入。我们检查用户是否按下了 Ctrl+C. 如果是,则返回 tea.Quit 命令,该命令属于 tea.Cmd 类型,并告诉 Bubble Tea 退出应用程序。

对于任何其他类型的输入,我们不做任何处理,只是按原样返回模型。但是,如果我们正在执行 UI 导航等操作,则会更改模型上的一些字段,然后返回它,从而导致 UI 更新。

⏭️ 下一站:菜单组件

在下一篇文章中,我们将学习如何创建一个菜单组件,并让我们的应用程序更加生动有趣!敬请期待!

0 0 投票数
Article Rating
订阅评论
提醒
0 评论
最多投票
最新 最旧
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x