参考react 实现一个golang gui

看到yoga 布局引擎,让我有个想法实现一个golang gui。 下面就是我的思路实现

Tenon

Go 语言的声明式、React 风格 GUI 工具包 ------ 函数组件 + Hooks,基于 Yoga flex 布局与 Gio 渲染。


把 React 的模型带到 Go 原生 GUI:

  • 函数组件 + Hooks ------ UseState/UseEffect/UseReducer/UseMemo/UseCallback/UseRef/UseContext。无 class、无手动失效。
  • 自动、局部重渲染 ------ setter 只重渲染它所属的组件(fiber)。
  • HTML 风格元素 ------ Div/Span/Button/Input/Img/Text/ScrollView/Portal/Fragment。
  • Yoga flex 布局,Gio 渲染(GPU 加速的矢量路径与文字;抗锯齿、高分屏自适应)。
  • 开箱即用 ------ 动画(补间/进出场/FLIP)、变换、拖拽/悬停/键盘,基础组件 kit,以及一套 shadcn/ui 风格组件库(~41 个)。

内部是类 React 的三棵树:不可变 Node 描述 → 持久 Fiber(身份 + hooks)→ renderNode(yoga 节点 + 绘制)。布局增量:纯绘制更新不重算布局。

快速上手

bash 复制代码
go get github.com/sjm1327605995/tenon
go 复制代码
package main

import (
	"fmt"
	ui "github.com/sjm1327605995/tenon/pkg/ui"
)

func Counter(_ struct{}) *ui.Node {
	count, setCount := ui.UseState(0)
	return ui.Div(
		ui.Style(ui.Row, ui.Gap(16), ui.Padding(24), ui.ItemsCenter),
		ui.Button(ui.OnClick(func() { setCount(count - 1) }), ui.Text("-")),
		ui.Text(fmt.Sprintf("%d", count), ui.FontSize(32)),
		ui.Button(ui.OnClick(func() { setCount(count + 1) }), ui.Text("+")),
	)
}

func main() { ui.Run(ui.Use(Counter, struct{}{})) }

包结构

包 说明
pkg/ui 引擎 + 元素、Hooks、样式、动画、输入。
pkg/shadcn 基于 pkg/ui 的 shadcn/ui 风格组件库(Button/Card/Dialog/Select/Table/Toast...)。
pkg/router 栈式导航(具名路由 + 参数,Push/Pop/Replace、返回)------ React Navigation 风格,纯建立在 hooks 之上。
yoga 纯 Go 的 Yoga flex 布局引擎移植。
pkg/font 字体加载/测量(内置支持 CJK 的字体)。

示例

  • go run ./example/accordion ------ 一个 shadcn/ui 风格的小型 文档站 。左侧分组侧栏列出 17 个组件(点击切换);右侧是该组件的文档页(面包屑、标题栏、框架标签、实时可交互预览、安装区),放在 ScrollView 里、各区块随滚动淡入;底部开关切换明暗主题。
  • go run ./example/router ------ 用 pkg/router 做的 栈式导航 :一个收件箱,点邮件 Push 进详情屏;详情屏可 Pop 返回,并用 Replace 原地换成下一封而不加深栈。

后台更新

渲染是单线程的。后台 goroutine 里更新界面请用 ui.Post 包裹:

go 复制代码
go func() {
	data := fetch()
	ui.Post(func() { setData(data) }) // 回到渲染线程,安全
}()
相关推荐
皓月盈江1 小时前
GO使用Gin框架实现模板定义、解析与渲染
linux·golang·go·gin·定义模板·解析模板·渲染模板
我不会起名字3223 小时前
Redis 从 0 到 1(十九):JWT 还是 Redis Session?登录态存储的 Go 实现
redis·go·jwt·session·登录态
hourinX3 小时前
一张只有两种颜色的头像,为什么还要用 NRGBA?—— `gdenticon` 重构日志(四)
go
我不会起名字3223 小时前
Redis 从 0 到 1(十六):1 亿 UV 只占 12KB?计数去重三件套的 Go 实测
redis·go·uv·布隆过滤器·hyperloglog
FungLeo6 小时前
成为全栈·Go 后端篇·换后端不改前端:四个客户端如何做兼容回归
flutter·golang·go·成为全栈·兼容测试·taro 小程序
我不会起名字3226 小时前
Redis 从 0 到 1(二十一):P99 涨到 80ms,慢查询日志 5 步定位法 + Go 采集器
redis·go·慢查询·排查·slowlog
我不会起名字3227 小时前
Redis 从 0 到 1(十八):List、Pub/Sub、Stream 为什么生产只用 Stream?Go 实操
redis·消息队列·go·stream·pub/sub
孔令飞7 小时前
AI 岗位类别、要求、技能及学习路线
云原生·go·ai编程
君万1 天前
【LeetCode每日一题】回溯:46. 全排列 78. 子集 39. 组合总和
算法·leetcode·go
vipxieliang1 天前
Go 逃逸分析与内存优化:从读懂 -gcflags=“-m“ 到降低 GC 压力
go