🵠一æ¯ä»£ç å†²æ³¡çš„çç å¥¶èŒ¶ï¼š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 是什么呢?

Model 是 Bubble Tea 的核心接å£ï¼Œå®ƒå®šä¹‰äº†ä¸‰ä¸ªæ–¹æ³•:

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 中一个很酷的抽象,它将整个 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 更新。

â­ï¸ 下一站:èœå•组件

在下一篇文章中,我们将学习如何创建一个èœå•ç»„ä»¶ï¼Œå¹¶è®©æˆ‘ä»¬çš„åº”ç”¨ç¨‹åºæ›´åŠ ç”ŸåŠ¨æœ‰è¶£ï¼æ•¬è¯·æœŸå¾…ï¼

留下评论