update
基于 Gio 的轻量 UI 框架:使用 JS/JSX 描述 UI(返回一棵 VDOM JSON),由 Go/Gio 渲染为原生窗口界面,并把点击/输入事件回传到 JS 驱动重渲染。
当前模块名:gitlink.org.cn/nanakura/goxui
gitlink.org.cn/nanakura/goxui
App()
onClick
onChange
onSubmit
import
go run ./cmd/jsgui -script examples/counter.js
Windows 下请确保 gcc/clang 可用:
gcc
clang
where gcc go env CGO_ENABLED CC CXX
运行:
$env:CGO_ENABLED="1" go run -tags quickjs ./cmd/jsgui -script examples/counter.jsx
运行 Todo 示例(包含多文件 import、弹窗、输入框):
$env:CGO_ENABLED="1" go run -tags quickjs ./cmd/jsgui -script examples/todo/app.jsx
运行 Go API 示例(在 JSX 中调用 Go 宿主函数):
$env:CGO_ENABLED="1" go run -tags quickjs ./cmd/jsgui -script examples/goapi/app.jsx
项目内置示例:
examples/counter.js
examples/counter.jsx
-tags quickjs
examples/todo/app.jsx
examples/todo/components/*
examples/im/app.jsx
examples/widgets/app.jsx
examples/hurlmgr_mock/app.jsx
examples/flow/app.jsx
JSX 的 import 行为:
.jsx/.tsx
Format=IIFE
GlobalName=__jsgui
Target=ES2020
import "./components/XXX.jsx"
JS 侧 App() 需要返回一棵可 JSON 序列化的树结构:
{ type: "View" | "Text" | "Button" | "Input" | "Modal", props: { ... }, children: [ ... ] }
View
props.direction === "row"
style.flexDirection === "row"
Text
props.text
Button
props.onClick
Input
props.value
props.placeholder
props.onChange(e)
e.value
props.onSubmit(e)
props.submit=true
submit
props.multiline
Modal
props.open
<Modal open> {baseUI} {dialogUI} </Modal>
ScrollView
Checkbox
props.checked: boolean
props.text: string
e.checked
Switch
Slider
props.min/max/value: number
Image
props.src: string
props.fit: "contain"|"cover"|"fill"|"scaleDown"|"unscaled"
Splitter
props.direction: "horizontal"|"vertical"
props.ratio: number
Tabs
Tab
Tabs.props.activeKey: string
Tabs.props.onChange(e)
e.key
Tabs.props.onClose(e)
Tab.props.title: string
Tab.props.closable: boolean
TextArea
props.value/placeholder/onChange
props.readOnly: boolean
Flow
props.nodes: Array<{id,title,x,y}>
props.edges: Array<{from,to}>
e.nodes/e.edges
运行时会注入一个全局对象 go,用于从 JS/JSX 调用 Go 实现的函数(示例见 examples/goapi/app.jsx)。
go
examples/goapi/app.jsx
当前内置函数:
go.now(): string
go.sha256(text: string): string
go.add(a: number, b: number): number
自定义注册函数(示例,使用 Ctx 注册并注入到引擎):
package main import ( "gitlink.org.cn/nanakura/goxui/host" "gitlink.org.cn/nanakura/goxui/internal/js" ) func Add(a, b int) int { return a + b } func main() { h := host.NewCtx() _ = h.Register("Add", Add) _ = h.Register("Other", func(s string) string { return "hi " + s }) engine := js.NewEngineWithHost(h) _ = engine.LoadPrelude() _ = engine.EvalFile("examples/goapi/app.jsx") }
注册后即可在 JSX/JS 中调用:
go.Add(2, 40); // 42
目前支持的 style 字段(通过 props.style 传入):
props.style
padding
margin
gap
width
height
flex
flexDirection
row
column
background
#RRGGBB
#RRGGBBAA
color
fontSize
borderRadius
RenderJSON()
internal/vdom.Node
__callHandler(handlerID, event)
quickjs+cgo
.js
fs
goxui
基于 Gio 的轻量 UI 框架:使用 JS/JSX 描述 UI(返回一棵 VDOM JSON),由 Go/Gio 渲染为原生窗口界面,并把点击/输入事件回传到 JS 驱动重渲染。
当前模块名:
gitlink.org.cn/nanakura/goxui特性
App()返回 VDOM(JSON)onClick/onChange/onSubmit回调由 Go 触发并回传事件 JSONimport其他 JSX/TSX,启动时由 esbuild 打包为单文件执行快速开始
运行 JS 示例(无需 CGO)
运行 JSX 示例(需要 QuickJS + CGO)
Windows 下请确保
gcc/clang可用:运行:
运行 Todo 示例(包含多文件 import、弹窗、输入框):
运行 Go API 示例(在 JSX 中调用 Go 宿主函数):
示例说明
项目内置示例:
examples/counter.js:最小 JS 示例(ES5)examples/counter.jsx:JSX 示例(需要-tags quickjs)examples/todo/app.jsx:Todo 管理应用(组件拆分在examples/todo/components/*)examples/im/app.jsx:Telegram 风 IM(假数据、多会话、多用户、气泡)examples/widgets/app.jsx:更多组件演示(ScrollView/Checkbox/Switch/Slider/Image)examples/hurlmgr_mock/app.jsx:Hurl Manager UI Mock(Splitter/Tabs/TextArea)examples/flow/app.jsx:Flow 编排控件 MVP(拖拽节点、点击连线、平移画布)JSX 的
import行为:.jsx/.tsx通过 esbuild 进行 bundle(Format=IIFE,GlobalName=__jsgui,Target=ES2020)import "./components/XXX.jsx"会被打包进同一份输出脚本,再交给 QuickJS 执行VDOM 约定
JS 侧
App()需要返回一棵可 JSON 序列化的树结构:内置组件
Viewprops.direction === "row"或style.flexDirection === "row"为横向Textprops.text文本内容Buttonprops.text按钮文案props.onClick点击回调(由 JSX runtime 编译成 handler id)Inputprops.value当前文本props.placeholder占位提示props.onChange(e)输入变化回调,e.value为新文本props.onSubmit(e)提交回调(需要props.submit=true或在 JSX 中显式传submit)props.multiline多行(默认单行)Modalprops.open是否打开<Modal open> {baseUI} {dialogUI} </Modal>ScrollViewCheckboxprops.checked: booleanprops.text: stringprops.onChange(e),e.checked为新值Switchprops.checked: booleanprops.text: stringprops.onChange(e),e.checked为新值Sliderprops.min/max/value: numberprops.onChange(e),e.value为新值(float)Imageprops.src: string(本地文件路径)props.fit: "contain"|"cover"|"fill"|"scaleDown"|"unscaled"Splitterprops.direction: "horizontal"|"vertical"(默认 horizontal)props.ratio: number(初始比例,0~1)Tabs/TabTabs.props.activeKey: string当前 Tab keyTabs.props.onChange(e),e.key为切换目标Tabs.props.onClose(e),e.key为关闭目标Tab.props.title: string标题Tab.props.closable: boolean是否可关闭(默认 true)TextAreaprops.value/placeholder/onChange与 Input 相同props.readOnly: boolean只读Flowprops.nodes: Array<{id,title,x,y}>props.edges: Array<{from,to}>props.onChange(e),e.nodes/e.edges为更新后的数据JS 调用 Go(宿主函数)
运行时会注入一个全局对象
go,用于从 JS/JSX 调用 Go 实现的函数(示例见examples/goapi/app.jsx)。当前内置函数:
go.now(): string返回 RFC3339 时间字符串go.sha256(text: string): string返回 hex 编码的 sha256go.add(a: number, b: number): number返回 a+b自定义注册函数(示例,使用 Ctx 注册并注入到引擎):
注册后即可在 JSX/JS 中调用:
style 支持
目前支持的 style 字段(通过
props.style传入):padding/margin/gapwidth/heightflex(仅影响View的子元素在 flex 布局中的比例)flexDirection(row/column)background(#RRGGBB或#RRGGBBAA)color(文本/前景色,#RRGGBB或#RRGGBBAA)fontSizeborderRadius构建与运行原理(简述)
RenderJSON()取得 VDOM JSONinternal/vdom.Node,用 Gio 渲染__callHandler(handlerID, event)RenderJSON(),实现重渲染限制与注意事项
quickjs+cgo构建下仅适合运行 ES5.js;.jsx/.tsx会提示使用-tags quickjsfs等);JSX 只是“描述 UI 的脚本”,不是浏览器环境