Help us shape the future of Go! Take the 2026 Go Developer Survey

Gox

module
v0.15.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT

README

Gox

用 Go 从零实现的 JavaScript 运行时:词法分析 → 语法分析 → 字节码编译 → 栈式虚拟机执行。 单二进制、零 cgo、零外部运行时依赖,自带软件光栅化 GUI 与脚本打包器。

Go License npm Platform Release test262 language test262 built-ins

📖 官网与使用教程 | GUI 开发指南 | npm 包

English version: README.en.md。两份 README 同步维护,描述同一个构建。


目录


概览

Gox 是一个用纯 Go 实现的 JavaScript(ES6+ 子集)运行时。整条编译管线 —— 词法分析、语法分析、 字节码生成、栈式虚拟机 —— 都从零实现,不依赖 V8/QuickJS 等任何现成引擎,也不依赖 cgo。

在此之上,Gox 补齐了脚本语言通常缺失的那一层:一套自研的桌面 GUI 渲染层(纯 Go 软件光栅化, flex 布局 + JSX + 信号驱动更新,win32 / X11 / cocoa 窗口后端)、一组宿主能力模块(文件、HTTP、剪贴板、 原生对话框、持久化存储),以及把脚本打包成独立可执行文件的工具链。

它的定位是"小而完整":一个 go build 得到一个可执行文件,可以把 JS 当脚本层嵌进 Go 程序, 也可以直接用它写带界面的小工具并打包分发。

设计取向
取向 做法 代价
单二进制分发 静态编译、无动态库依赖;跨平台只需换 GOOS/GOARCH,无需目标机工具链 二进制体积较大
零 cgo 后端直连系统 API(Windows 走 syscall、Linux 走 X 协议、macOS 走 cocoa/purego) 不支持 -race 检测
GUI 不是外挂 渲染、布局、命中测试、脏矩形重绘全在运行时内部,脚本侧只有 JSX 与信号 渲染能力受软件光栅化上限约束
确定性单线程 VM 单线程执行,网络 I/O 在 goroutine 中完成后投递回主线程,回调无需加锁 无真正的并行执行
缺能力明说缺 宿主能力走 canIUse() + 8 个统一错误码,不返回假数据、不假装在播 某些场景下比"尽力而为"更早暴露问题
Gox 不适合

把边界写在前面,比让读者踩坑再回头更省事:

  • 不适合做 Web 后端或替代 Node.js —— 不支持 CommonJS(require)与 npm 生态的大多数包,见 兼容性矩阵。
  • 不适合 CPU 密集的计算任务 —— 纯解释器,无 JIT,fib28 比 goja 慢约 3 倍、比 V8 慢一个数量级,见 性能。
  • 不适合需要完整 ES 规范的生产系统 —— test262 built-ins 合规率 62%,Temporal / RegExp 等子集仍有成块缺口。
  • 不适合"一次写完到处跑"的幻想 —— 三端形态(折叠屏、安全区、权限)差异真实存在,需要按平台适配。

适用场景:用 JS 写跨平台桌面小工具;把 JS 作为配置与插件层嵌入 Go 应用;学习编译器与虚拟机实现。


项目状态

Gox 处于 0.x 活跃开发期 —— 公共 API 可能在 minor 版本间发生破坏性变更(0.x 下 minor 即破坏位)。

test262 合规率

官方 ECMAScript 一致性套件,由 CI 每周定时跑批并自动回写徽章(docs/test262-*.json):

套件 合规率 口径
language 97.0% 语言语义(语句 / 表达式 / 模块 / 函数 / class 等)
built-ins 62.1% 内置对象(Array / String / Temporal / TypedArray …),14804 / 23823

两个套件分母不同、构成完全不同,所以各守一条独立的下限基线;intl402(需 i18n)Gox 未实现,不在 runner 的套件内。 合规率是趋势观测而非验收门禁 —— 失败面按「目录族 × 失败形态」聚类成 502 个簇,排期按簇开单。 完整口径与跑法见 官网 · 性能与合规。

平台支持矩阵
平台 运行时 GUI 窗口后端 CI 覆盖
Windows x64 ✅ win32(纯 syscall) ci.yml 构建 + 测试
Linux x64 / arm64 ✅ X11(纯协议) ci.yml 构建 + 测试
macOS x64 / arm64 ✅ cocoa(purego) ci.yml 构建 + 测试
Android arm64 ✅ 预编译库 壳工程(Kotlin 宿主) mobile-smoke.yml + 折叠屏模拟器回归
iOS arm64 ✅ 预编译库 壳工程(Swift 宿主) mobile-smoke.yml 模拟器三机型;真机验收待做
鸿蒙 arm64 ⚠️ 交叉编译通过 壳工程(ArkTS),六个能力模块仍是桩 构建通过;HAP 签名是硬阻塞,模拟器验收待做
已知缺口
  • 富文本(行内混排样式)尚未封装;虚拟化长列表已由 <scroll vlist> 落地
  • 原生能力层中依赖真机的部分(相机 / 定位 / 相册 / 权限)桌面端诚实报 unsupported
  • CommonJS / require 明确不做;npm 生态仅 uuid 等极少数库部分可用
  • 逐项见 docs/gui-guide.md 与官网 限制与常见误区

快速开始

环境要求
场景 要求
从源码构建 Go 1.26.2 或更高(零 cgo,无需 C 工具链)
仅运行脚本 无 —— npm 包已内置各平台预编译二进制,只需 Node.js ≥ 14
GUI 应用 Windows(win32)、Linux(X11)或 macOS(cocoa)
三种装入方式
# 1) npm —— 最省事,五平台预编译二进制随包分发
npm i -g @goxjs/goxjs    # 安装后可直接使用 goxjs 命令
npx goxjs app.js         # 或不安装,直接运行

# 2) 源码构建 —— 需要一个 Go 工具链
git clone https://github.com/14752222/Gox.git
cd Gox
go build ./cmd/gox       # 生成可执行文件;Windows 下为 gox.exe

# 3) go install —— 直接装进 GOBIN(需先发布过 tag)
go install github.com/14752222/Gox/cmd/gox@latest

npm/ · website/ · gox-logo-concepts/ 是三个独立仓库(gox-npm · gox-website · gox-logo-concepts),2026-10-12 起不再以 git submodule 挂载,克隆主仓不需要 --recurse-submodules。CI 构建时会自动 checkout npm/ 与 website/;本地要跑 scripts/check-registries.py 的话,先 git clone https://github.com/14752222/gox-npm npm(否则读不到 npm/package.json)。

交互式 REPL
./gox                # Unix;Windows 下为 gox.exe
Gox REPL (ES6 subset)
Type :exit to quit, :help for help

> let x = 10
> let y = 20
> x + y
  30
> [1, 2, 3].map(v => v * 2)
  [2, 4, 6]

REPL 命令::help 查看帮助、:clear 重置环境、:exit 退出。

运行脚本
./gox example.js    # npm 安装的 goxjs 命令用法相同

脚本执行完毕后会回显最后一个顶层表达式的值(undefined 除外),并等待定时器与异步回调 全部执行完再退出。


首屏示例:单双栏自适应列表页

窗口宽度 < 600dp 时单栏,拖宽到 ≥ 600dp 自动变双栏(左侧列表 + 右侧详情)。 拖拽窗口边缘即触发,不用监听 resize,也不用轮询尺寸。完整可跑文件: testdata/adaptive_list_demo.js(gox testdata/adaptive_list_demo.js)。

import { h, render } from "gx/gfx";
import { createSignal } from "gx/solid";
import { useBreakpoint, matchBreakpoint } from "gx/viewport";
import { Switch, Match } from "gx/view";

const bp = useBreakpoint();                 // () => "sm" | "md" | "lg" | "xl"(订阅型)
const [selected, setSelected] = createSignal(0);

const ITEMS = [
  { id: "a", title: "编译管线", desc: "词法 → 语法 → 字节码 → 栈式 VM,160 个操作码。" },
  { id: "b", title: "软件光栅化 GUI", desc: "flex 布局 + JSX + 信号驱动,三后端。" },
  { id: "c", title: "脚本打包器", desc: "把 .js 打成单个可执行文件。" },
];

// 布局分支由断点名声明(一张 sm/md/lg/xl 的表),
// 而不是把同一个宽度判断散落在 N 处 if 里 —— 加一档只改表,不改判断。
const layout = () => {
  bp();   // ← 无条件读一次 = 订阅登记。删掉这行,整页不再自适应(无警告)
  return matchBreakpoint({
    sm: { cols: 1, side: false },   // <600dp  单栏
    md: { cols: 2, side: true },    // >=600dp 双栏
    lg: { cols: 2, side: true },
    xl: { cols: 2, side: true },
  });
};

const List = () => (
  <column gap={6}>
    <view each={ITEMS} key="id">
      {(it, i) => (
        <row gap={6} padding={8} radius={6}
             background={selected() === i ? "#dce8ff" : "#f2f5fa"}
             onClick={() => setSelected(i)}>
          <text font={13} color="#334">{it.title}</text>
        </row>
      )}
    </view>
  </column>
);

const Detail = () => (
  <column gap={6} padding={12} radius={8} background="#ffffff">
    <text font={14} color="#223">{ITEMS[selected()].title}</text>
    <text font={12} color="#667">{ITEMS[selected()].desc}</text>
  </column>
);

render(
  <window title="自适应列表页" width={520} height={420}>
    <column gap={10} padding={14}>
      <text font={15}>{() => `breakpoint = ${bp()}   →   ${layout().side ? "双栏" : "单栏"}`}</text>
      <Switch>
        <Match when={() => layout().side}>
          <row gap={10}>
            <column width={210} gap={6}><List /></column>
            <column flex={1}><Detail /></column>
          </row>
        </Match>
        <Match when={() => true}>
          <column gap={8}>
            <List />
            <Detail />
          </column>
        </Match>
      </Switch>
    </column>
  </window>
);

两个最容易踩的坑(示例注释里同样写明):

  1. 纯读数会短路掉订阅 —— breakpoint() / posture() / hasFold() 是纯读数、不订阅;只有 useBreakpoint() 这类两段式返回的取值函数才订阅。把纯读数写在三元的第一操作数或 && 左侧, 被短路的那支一次都没读 ⇒ 零依赖 ⇒ 之后宽度变了这一支永不重跑,没有任何警告。 gfx/adaptive_list_test.go 把这条钉成了断言:纯读数快照拖宽后仍停在首帧的值。
  2. 坐标单位是两套 —— reservedRegions() 的 x/y/width/height 是设备像素(折痕、挖孔), safeAreaStyle() 与脚本显式写的尺寸属性是 dp(内核每帧乘 Display.Scale)。 折痕避让时不要把前者直接喂给布局属性,先除 pixelRatio。

能力总览

能力域 内容 文档
语言运行时 160 个操作码、定长 3 字节指令;ES6+ 语言子集;完整内置对象族 语言与内置对象
GUI 渲染层 63 个内置元素、flex 布局、JSX、脏矩形局部重绘、win32 / X11 / cocoa 三后端 docs/gui-guide.md
宿主能力 全局注入 fs / path / http / fetch / process / stats / crypto / hash;gx/* 内置模块 23 个 宿主能力模块
移动端与形态 Android / iOS / 鸿蒙壳工程 + gx/screen 折叠屏与安全区语义 docs/foldable-adaptation.md
工具链 REPL、gox create 脚手架、gox dev 热更新、gox cert / gox build / gox sync / gox icon、jsbuild 打包 打包与工具链

语言与内置对象

语法子集

let/const/var、函数与箭头函数、闭包、class(含私有字段 #name)、async/await、 解构赋值、剩余/默认参数、展开、模板字符串、for...of、for...in、try/catch/throw、 可选链 ?.、空值合并 ??、ES 模块 import/export,以及 JSX 语法 (编译期降级为 h(tag, props, ...children) 调用)。

let x = 10
let y = 20
console.log(x + y)                                                 // 30
console.log([1, 2, 3].map(v => v * 2))                             // [2, 4, 6]
console.log([1, 2, 3].filter(v => v > 1).reduce((a, b) => a + b))  // 5
console.log(Math.hypot(3, 4))                                      // 5

let name = "Gox"
console.log(`Hello, ${name}!`)                                     // Hello, Gox!

let [a, b] = [1, 2]                                                // 解构赋值
console.log(a + b)                                                 // 3
class Point {
  constructor(x, y) { this.x = x; this.y = y }
  norm() { return Math.hypot(this.x, this.y) }
}
console.log(new Point(3, 4).norm())       // 5

async function main() {
  let v = await delay(10, "timer done")   // delay(ms, value) 返回 Promise
  console.log(v)
}
main()
5
Promise { <pending> }     ← 顶层回显:main() 调用的返回值
timer done                ← 定时器到期后,await 继续执行

语法提示:异步函数写 async function 或 async 箭头 async () => {} 都行;顶层不能 await。

ES 模块 —— lib.js:

export const PI = 3.14
export function double(x) { return x * 2 }

main.js:

import { PI, double } from "./lib.js"
setTimeout(() => console.log("tick"), 10)
console.log(double(PI))
6.28
tick
内置对象

Array / String / Number / Object / Boolean / Math / JSON / Map / Set / WeakMap / WeakSet / Symbol / BigInt / RegExp / Proxy / Reflect / Iterator / Promise / ArrayBuffer / DataView(TypedArray 家族)/ WeakRef / FinalizationRegistry / 完整错误类型族 / Temporal(现代日期时间 API,本运行时不含 Date)。

响应式编程

Dart GetX 风格的 obs / computed / ever / once:

let count = obs(0)
ever(count, v => console.log("count =", v))   // 订阅时立即以当前值回调一次
count.value = 1
count.value = 2
count.value = 2   // 值未变化,不触发通知
count = 0
count = 1
count = 2
2                  ← 顶层回显:最后一条赋值表达式的值

以及 SolidJS 风格的 gx/solid 信号(createSignal / createEffect / createMemo / createResource / onMount / onCleanup)。


GUI 桌面应用

gx/gfx + gx/solid 提供 JSX 声明式 UI 与信号驱动的响应式更新,渲染器为纯 Go 软件光栅化 (无 cgo、无动态库依赖)。

用脚手架起工程
gox create my-app                          # 同为 goxjs create / npx @goxjs/goxjs create
gox create my-app --ts                     # TypeScript/TSX 版: .tsx 直接跑, 无 node 端构建
cd my-app && npm install && npm run dev

生成 package.json、入口 src/main.js、根组件 src/app.js、共享状态 src/store.js、 设计令牌 src/theme.js 与 src/components/ 下三个示例组件(计数器 / 待办列表 / 多状态), 把信号、元素级指令(each / show)、model 双向绑定与 Switch / Match 各演示一遍。 --ts 生成 TypeScript 版:入口改 src/main.tsx、附 tsconfig.json 与 src/gox.d.ts (IDE 类型提示),源码在加载时自动完成类型剥离(内嵌 esbuild 转译,JSX 原样保留)—— 没有 node 端构建步骤,类型检查交给 IDE,运行时零开销。

模板是 scaffold/template/ 下的真实文件,由 go:embed 嵌进二进制 (改模板要重新编译才生效;scaffold/scaffold_test.go 会保证生成出来的 JS/TS 仍能过 转译 + lexer → parser → compiler,gfx/scaffold_project_test.go 会真的把它挂载起来点一遍)。

最小手写版
import { h, render, createSignal } from "gox"        // 聚合入口:所有 gx/* 导出的并集
// 等价的细分写法:import { h, render } from "gx/gfx"; import { createSignal } from "gx/solid"

const [count, setCount] = createSignal(0)

render(
  <window title="Counter" width={400} height={300}>
    <column gap={8} padding={16}>
      <text font={20}>计数:{ count()}</text>
      <button onClick={() => setCount(c => c + 1)}>加一</button>
    </column>
  </window>
)
./gox counter.js          # 直接运行,弹出 400x300 窗口

点击按钮 → 信号更新 → 依赖它的属性/文本节点标脏 → 脏矩形合并后只重绘受影响区域。render() 返回窗口句柄,可调用多次开多窗口(各有独立元素树与焦点,全关才退出进程)。

GUI 能力清单
  • 63 个内置元素 —— 布局(column / row / grid / scroll / view)、表单(input / textarea / select / slider / checkbox / radio / switch)、数据展示(table / tree / list-item / tabs / pagination)、反馈(alert / tag / badge / avatar / empty / icon / spinner / skeleton / progress / tooltip)、弹层(dialog / drawer / toast)、 媒体(<video> / <audio>)、自绘画布、自绘菜单栏与右键菜单
  • 声明式界面是元素级指令 —— model 双向绑定、each 列表(keyed 复用)、show 条件保活显隐, 多分支用 gx/view 的 Switch + Match
  • 路由与屏幕 —— gx/router(路由表 / 参数匹配 / 三级守卫 / 懒加载 / 历史栈 / 多窗口作用域 / 折叠屏双栏)与 gx/screen(多显示器 / 窗口显示信息)
  • 媒体组件真的能播 —— 内核只定义宿主契约(nativeVideoHost / nativeAudioHost): 能挂载、能收事件、playing / muted / loop / volume / fit 全受控,后端没这块能力就明说缺 (封面占位 + 一次 onError({code:"unsupported"}) + canIUse("video"/"audio") 照实回答 false)。 Linux 默认构建挂 gfx/softmedia 软件后端:视频 GIF(含 disposal 合成)与 MJPEG,音频 WAV (PCM 8/16/24/32 位 + IEEE float),音频经 PulseAudio purego dlopen 输出(零 cgo)。 h264 / aac / mp3 留给平台媒体层,边界见 docs/media-decision.md

完整参考 —— 事件模型、内置元素属性表、弹性布局与装饰绘制、受控组件与输入法、过渡动画、 原生对话框 / 剪贴板 / 持久化存储 / 菜单栏、多窗口语义与平台差异 —— 见 docs/gui-guide.md,可直接运行的示例见 testdata/(counter_demo.js、form_demo.js、multiwindow_demo.js、 menu_demo.js、view_demo.js、router_demo.js、router_window_demo.js 等 108 个)。

路由与屏幕适配另有一份专门手册:docs/gui-router.md。


宿主能力模块

全局对象(无需 import)

fs / path / http / fetch / process / stats / crypto / hash:

const text = fs.readFileSync("a.txt")          // 同步:失败抛 JS 异常
const data = await fs.readFile("a.txt")        // 异步:无回调时返回 Promise
fs.mkdirSync("a/b", { recursive: true })

const server = http.createServer(function (req, res) {
  res.writeHead(200, { "Content-Type": "text/plain" })
  res.end("hello")
})
server.listen(0, function () { console.log(server.port) })

const res = await fetch("https://example.com")
console.log(res.status, await res.text())

异步 I/O 的实现模型是跨 goroutine I/O + 投递回 VM 单线程:网络读写不触碰 VM 状态,结果经 零延时定时器回到事件循环执行 JS 回调,因此回调里访问 VM 无需加锁。

一份完整的 HTTP 示例(起服务于端口 0、路由/查询参数/JSON 请求体/404-405-500 各状态码、 回调式 http.get · http.request 与 Promise 式 fetch 对照、异步响应与 server.close 收尾): testdata/http_demo.js —— gox testdata/http_demo.js 即可运行。

摘要与随机数 —— crypto 与 hash 底层走 Go 标准库 crypto/*,零 cgo:

hash("gox");                                 // 同步 → 小写 hex (缺省 SHA-256)
hash("gox", "SHA-512");                      // 算法名大小写/连字符不敏感
const ab = await crypto.subtle.digest("SHA-256", "gox");   // Promise → ArrayBuffer (32 字节)
crypto.randomUUID();                                       // RFC 4122 v4
crypto.getRandomValues(new Uint8Array(16));                // 原地填充, 上限 64KB
项 说明
算法 hash() 支持 MD5 / SHA-1 / SHA-256 / SHA-512;crypto.subtle.digest() 只认 SHA-1 / SHA-256 / SHA-512
参数序 hash(data, algo) 而 crypto.subtle.digest(algo, data)
不做的事 加解密 / 签名 / KDF 不在本模块内
错误 不支持的算法或入参一律 TypeError,不会静默回退
gx/* 内置模块(需要 import)

注册表共 23 个模块:gox(聚合入口)、gx/solid、gx/gfx、gx/view、gx/router、 gx/screen、gx/viewport、gx/dialog、gx/storage、gx/update、gx/crypto、gx/theme、 gx/dev、gx/a11y、gx/device、gx/app、gx/geo、gx/media、gx/permission, 以及全局侧同样可 import 的 fs / http / path / process。

原生能力层六个模块共用一个宿主契约(NativeHost)。三种调用形态要在写代码时区分开:

import { deviceInfo, battery, isOnline, canIUse } from "gx/device";   // 拉取型 + 上报型
import { getLocation, watchLocation } from "gx/geo";
import { takePhoto } from "gx/media";                                  // 动作型

const info = deviceInfo();                    // 同步可得
console.log(info.platform, info.model, battery().level + "%", isOnline());

if (canIUse("camera")) {                      // 事前判断能力,不要靠 catch 兜底
  const photo = await takePhoto({ count: 1 });      // 失败会 reject,带 errCode
}

const stop = watchLocation((loc) => console.log(loc.latitude, loc.longitude));
try {
  await getLocation({ highAccuracy: true });
} catch (e) {
  if (e.errCode === "permission-denied") console.warn(e.message);
}

桌面后端直接实现其中能实现的部分(电量 / 网络 / 亮度 / 屏幕常亮 / 打开系统设置页), 给不出的一律诚实报 unsupported。移动端同样实现这套 NativeHost 契约,不必改内核 —— Android 壳(Kotlin)与 iOS 壳(Swift)的六个模块基本齐备:Android 已在模拟器上实测通过 (上屏 / 触摸 / IME / 安全区 / 折叠屏),iOS 壳工程与构建脚本(含 TestFlight 打包)齐备、真机验收待做; 鸿蒙壳(ArkTS)目前只通了安全区与折叠上报两条纯上报通道,六个能力模块仍是桩。 逐方法(模块 × 平台)状态表、壳构建说明与首帧自检清单见 app/NATIVE-HOST.md; 三种形态的完整演示:testdata/native_demo.js。

三端验证范围(CI 矩阵口径)—— 能力矩阵按实际验证面写,不夸:

平台 构建冒烟(每次 push) 设备回归 折叠语义
Android mobile-smoke.yml 的 android job:libgox.so → assembleDebug APK android-fold-emulator job:pixel_fold 模拟器(API 34+,真能切铰链)上跑 CLOSED → HALF_FOLDED → OPENED 矩阵 + 截屏存档;真机侧 scripts/mobile-device-accept.sh --rounds 20 ✅ 模拟器已覆盖折叠态切换
iOS mobile-smoke.yml 的 ios job:xcodebuild 模拟器包 ios-device-matrix job:iPhone 16 / 16 Pro Max / iPad 三机型 × 竖横屏,simctl 截屏存档 ⚠️ 模拟器没有折叠屏:reservedRegions(iOS 27.1+)在模拟器只报 flat;真机验收待做
鸿蒙 交叉编译 + 宿主契约测试 + assembleHap 构建通过 — ❌ 模拟器验收待做(HAP 签名是硬阻塞);不写「已支持鸿蒙」

打包与工具链

jsbuild:脚本 → 独立可执行文件

jsbuild(packager/)把入口脚本及其相对 import 的模块嵌入一个生成的 Go 工程,编译成单文件 可执行程序,自带完整运行时:

go run ./packager app.js -o app.exe                    # CLI 应用
go run ./packager counter.js --gui -o counter.exe      # GUI 应用
go run ./packager app.js --gui --target linux/amd64    # 纯 Go 交叉编译,无需目标机工具链

各平台的分发注意事项(Windows 图标与签名、Linux 打包格式、macOS .app bundle)见 docs/desktop-distribution.md。

命令行参数:打包产物与 gox <file> <args> 保持同一口径 —— process.argv 恒为 [可执行文件, 脚本路径, ...用户参数],用户参数始终从索引 2 开始。 于是同一份脚本源码跑和打包跑都能用 process.argv[2] 取到第一个用户参数。

多平台配置(gox.json / 权限 / 图标 / 证书)

gox create 生成的工程自带 gox.json(应用名、appId、版本、权限、各平台子配置) 与完整的 Android / iOS / 桌面资源骨架 + 默认图标:

命令 作用
gox sync 把 gox.json 里的 permissions 幂等注入 AndroidManifest 与 iOS Info.plist
gox icon 以 assets/icon.png(1024×1024)为单源生成 Android mipmap、iOS AppIconSet、Windows .ico、macOS .icns、favicon
gox cert 一键生成平台签名证书(纯 Go 实现,无需 JDK/openssl):android / windows / harmony / ios
gox build <android|ios|windows|macos> 统一构建入口(sync → icon → 平台打包);--release 走签名发布包

字段说明、权限清单与图标规范见 docs/platform-config.md。 脚本运行时的权限模型(--allow-fs-write 等,默认最小权限)见 docs/permissions.md。

npm 包信息
项 值
包名 / 命令 @goxjs/goxjs → goxjs
内含平台 macOS x64 / arm64、Linux x64 / arm64、Windows x64
Node 版本 >=14(仅用于分发二进制,运行期不依赖 Node)
版本真源 gox-npm 的 package.json 的 version 字段
移动端子包 @goxjs/goxjs-mobile-<platform>-<abi>(4 个,按需安装,不在主包依赖里)

技术架构

编译与执行管线
 .js 源码
    │  lexer        词法分析
    ▼
 Token 流
    │  parser       语法分析(含 JSX 降级)
    ▼
 AST (ast/)
    │  compiler     符号表解析 + 字节码生成 (常量池)
    ▼
 字节码 (bytecode/)   定长 3 字节: [opcode 1B][operand 2B 大端],160 个操作码
    │  vm           栈式调用帧执行 + 定时器事件循环 + 模块加载
    ▼
 结果
关键设计决策
  • 定长指令编码 —— 无需变长解码状态机,直取操作数;常量池承担大对象引用。
  • 单线程 VM + 跨 goroutine 调度 —— http.createServer 等网络回调跨 goroutine 捕获后调度回 VM 单线程执行,无需锁。挂起任务保活事件循环,计数归零才允许进程退出。
  • 可选能力走可选接口 —— GUI 后端通过 Surface + WindowFactory 抽象,换后端零改动脚本; 平台独有能力(IME、剪贴板、原生对话框、视频 / 音频)按后端探测,缺失时诚实降级而非报错。
  • 固定分片保证合规率可复现 —— test262 runner 的分片数钉死 128 且与 -jobs 解耦: 引擎有包级可变状态,分片数若跟着并发度变,每个用例的"同进程邻居"就变,通过数会随 -jobs 翻转。
目录结构
目录 职责
lexer/ 词法分析器(含 JSX 词法支持)
parser/ 语法分析器,生成 AST(含 JSX 语法降级)
ast/ AST 节点定义
compiler/ AST → 字节码编译器(含符号表)
bytecode/ 操作码与字节码格式定义
vm/ 栈式字节码虚拟机(调用帧、模块加载、定时器调度)
object/ 运行时对象系统(Number / Array / Map / Promise / Observable…)
runtime/ 全局环境 Environment
stdlib/ 标准库与宿主 API 实现(含 gx/solid 响应式信号)
gfx/ 自研 GUI 渲染层(软件光栅化、布局、命中测试、win32 / X11 / cocoa 后端)
packager/ jsbuild 打包器(GUI 应用、交叉编译)
scaffold/ gox create 的项目脚手架:template/ 是真实文件(go:embed 进二进制)
tools/ 开发工具:tstransform/(TS/TSX → JS)、icongen/(图标)、certgen/(证书)、update/(自更新)、perf/(基准)
test/ 测试相关:bench/ 性能剖析基准
testdata/ 可直接运行的示例脚本(108 个)
app/ 移动端壳工程与 NATIVE-HOST.md 契约文档
docs/ 对外文档
scripts/ 构建与检查脚本:build-npm.sh、check-registries.py、check-imports.py、test262-cluster.py 等
.github/workflows/ CI:ci.yml 常规闸门、release.yml 发版、pages.yml 官网、test262.yml 合规率、mobile-smoke.yml 三端矩阵

性能

数字来自 CI runner 上的公开基准(bench-results/,AMD EPYC 7763)与官网实测页(Apple M2)。 含未达标项 —— 口径与阈值全部公开。

指标 gox goja(纯解释器对照) node / V8(JIT 对照)
启动耗时 4 ms 3 ms 25 ms
fib28 纯计算 631 ms 200 ms 34 ms
1 万定时器 168 ms 116 ms 139 ms
常驻内存 RSS 25 MB 10 MB 42 MB

结论:启动与内存与同类解释器同级;纯计算算力未达 goja 同级(fib28 慢约 3 倍), 这是纯解释器 + 无 JIT 的直接代价,不作为优化目标。UI 无 GC 毛刺达标 (重压聚合 p95 帧间隔 5.95ms ≤ 16.7ms;单次 GC 停顿 max 1.382ms ≤ 5ms)。

虚拟化长列表(<scroll vlist>)与行数脱钩:十万行首帧 89ms vs 全量 2187ms(约 24 倍), 滚动帧恒在 0.11~0.17ms vs 全量 999ms。

完整数字、跑法与判定阈值见 官网 · 性能与合规。


文档地图

下表只列 docs/ 里已定稿、对外发布的文档。

文档 内容
docs/tutorial.md 实战教程:API 调用与参数、内置模块导入、gox create 建工程、路由定义与注册
docs/gui-guide.md GUI 开发指南:元素/事件参考、布局、动画、宿主能力、示例索引
docs/gui-router.md 路由与屏幕适配手册
docs/gui-patterns.md 用户态模式手册(路由、状态、主题等惯用法)
docs/gui-model-binding.md model 双向绑定:接口设计、语义表、与 Vue 的对照、反例
docs/foldable-adaptation.md 折叠屏适配教程:纯读数短路订阅的坑、折痕避让单位换算、三端验证范围
docs/platform-config.md 多平台配置:gox.json 字段、权限清单、图标规范、gox build 各目标
docs/desktop-distribution.md 桌面应用分发:图标、签名、各平台打包格式
docs/js-runtime-api-tutorial.md 运行时 API 教程:函数类型、回调桥、内存管理、新增 API 的流程
docs/permissions.md 脚本运行时权限模型
docs/compat-matrix.md 主流 npm 库实测兼容性
docs/npm-release.md @goxjs/goxjs 发版手册
官网 安装、运行脚本、写 GUI 应用与打包的在线教程(中英双语)

开发与测试

go test ./...          # 运行全部测试(308 个测试文件 / 2419 个测试函数)
go run ./test/bench    # 生成 cpu.prof 性能剖析

由于零 cgo,go test -race 在本项目不可用(Go 会要求 cgo 支持)。

深入参与开发(新增标准库 API、理解回调桥与内存管理)请阅读 docs/js-runtime-api-tutorial.md。


发版流程

@goxjs/goxjs 由 GitHub Actions 自动发布(.github/workflows/release.yml), 认证走 npm Trusted Publishing (OIDC),仓库里不需要任何 secret / token:

# 1. 改版本号 —— npm/package.json 的 version 是唯一真源,npm/ 是独立仓库:
$EDITOR npm/package.json
git -C npm commit -am "chore(release): 0.15.0" && git -C npm push
# 2. 回主仓打 tag 并推送 —— tag 必须与 gox-npm 默认分支的版本一致
git tag v0.15.0 && git push Gox v0.15.0
# 3. 剩下交给 CI:交叉编译五平台二进制 → 打包校验 → OIDC 发布 → 自动打 tag + 建 Release
  • 触发:push 到 main、推送 v* 标签、发布 Release,以及手动 workflow_dispatch
  • 幂等:CI 先查 registry,该版本已存在则跳过并留一条 notice —— 重复推送不会误发
  • 版本号:日常提交不会触发真发布;cmd/gox/main.go 的 const version 与 npm/package.json 须同号(scripts/check-registries.py 卡死)
  • 完整手册:见 docs/npm-release.md

贡献指南

欢迎提交 Issue 与 Pull Request —— 完整的参与方式、本地闸门、与本仓库特有的 「改一处、同步多处」纪律,见 CONTRIBUTING.md; 行为准则见 CODE_OF_CONDUCT.md。首次贡献可从带 good first issue 标签的 issue 入手。

开发环境:Go 1.26.2+,零 cgo(无需 C 工具链)。提交前请确保:

gofmt -l .             # 应无输出
go build ./...
go vet ./...
go test ./...
python3 scripts/check-registries.py   # 注册表一致性(CI 同款闸门)
python3 scripts/check-imports.py      # 文档/示例 import 与导出表一致(CI 同款闸门)

提交规范:沿用仓库现有的 Conventional Commits 风格,scope 用受影响的模块或主题,中文描述:

feat(gfx): 新增 <slider> 的键盘调节支持
fix(vm): 修正 SET_INDEX 对非对象类型静默丢弃的问题
docs(gui-guide): 补充多窗口事件泵的轮询语义
ci(release): 打包闸改回 tar 校验,不再解析 npm 的输出

新增内置 GUI 组件时须同步四处(漏改会静默失效,不出编译错误):

位置 作用
gfx/node.go 的 knownTags 注册标签名,否则 h() 会打印"未知标签"警告
gfx/layout.go 的 intrinsicSize 声明固有尺寸
gfx/layout.go 的布局分派 声明子节点如何参与布局
gfx/raster.go 的 drawNode 实现绘制;被 case 截走的标签需自行补画 background / border

其他约定:

  • 事件回调统一经 callHandler 分发;涉及窗口的操作从节点出发经 appOfNode(n) 取所属窗口(支持多窗口)
  • gx/* 的 JSX 属性与子节点在调用当场求值一次 —— 需要响应式就必须传函数(value: () => sig()), 子节点写 {() => sig()} 而不是 {sig()}),传值只是一张快照 —— 而且子节点写成快照 是全静默的(属性误用有警告,子节点没有),见 docs/gui-guide.md §8.1
  • 示例脚本放在 testdata/ 并确保可直接运行

许可证

MIT License

Directories

Path Synopsis
Package bytecode 定义了虚拟机的字节码指令集。
Package bytecode 定义了虚拟机的字节码指令集。
cmd
gox command
cmd_build.go 实现 `gox build <target>`: 统一构建入口。
cmd_build.go 实现 `gox build <target>`: 统一构建入口。
goxwasm command
非 js/wasm 平台的占位:让 `go build ./...` / `go vet ./...` 在本机不至于因 "build constraints exclude all Go files" 报错。
非 js/wasm 平台的占位:让 `go build ./...` / `go vet ./...` 在本机不至于因 "build constraints exclude all Go files" 报错。
Package compiler 将 AST 编译为字节码。
Package compiler 将 AST 编译为字节码。
Package config 定义并解析 Gox 工程的项目级配置文件 gox.json。
Package config 定义并解析 Gox 工程的项目级配置文件 gox.json。
gfx
Package gfx 提供 Gox 的自研 GUI 渲染层 (P2: Windows 软件渲染)。
Package gfx 提供 Gox 的自研 GUI 渲染层 (P2: Windows 软件渲染)。
backend
Package backend 按构建平台选择窗口后端: 此文件在 Linux 构建时 注册 gfx/x11 (纯 Go X11 协议绑定 jezek/xgb, 无 cgo)。
Package backend 按构建平台选择窗口后端: 此文件在 Linux 构建时 注册 gfx/x11 (纯 Go X11 协议绑定 jezek/xgb, 无 cgo)。
bind
Package bind 是 gfx 的 JS 绑定层: 把内核能力以 object.RegisterBuiltinModule 注册成脚本可 import 的 "gx/xxx" 模块。
Package bind 是 gfx 的 JS 绑定层: 把内核能力以 object.RegisterBuiltinModule 注册成脚本可 import 的 "gx/xxx" 模块。
cocoa
Package cocoa 是 gfx 的 macOS 窗口后端 —— 基于 purego (纯 Go, 无 cgo) 的 objc runtime 桥, 直接驱动 AppKit。
Package cocoa 是 gfx 的 macOS 窗口后端 —— 基于 purego (纯 Go, 无 cgo) 的 objc runtime 桥, 直接驱动 AppKit。
gpu
Package gpu 的默认轨: 空实现。
Package gpu 的默认轨: 空实现。
hostbridge
Package hostbridge 是移动端三端 (android / ios / harmony) **共用**的宿主协议层。
Package hostbridge 是移动端三端 (android / ios / harmony) **共用**的宿主协议层。
hosttest
Package hosttest 是「宿主契约」的**平台无关**测试套件 (无 build tag)。
Package hosttest 是「宿主契约」的**平台无关**测试套件 (无 build tag)。
mobile
Package mobile 是移动端 (Android / iOS) 后端的**可移植内核部分**: 事件队列、触摸→指针映射、帧上传钩子、显示器 (density) 上报、事件泵唤醒。
Package mobile 是移动端 (Android / iOS) 后端的**可移植内核部分**: 事件队列、触摸→指针映射、帧上传钩子、显示器 (density) 上报、事件泵唤醒。
softmedia
Package softmedia 是 gfx 的**软件媒体后端** —— 用纯 Go 标准库可解码的 格式, 把 `<video>` / `<audio>` 从"契约 + 降级"变成"真的能播"。
Package softmedia 是 gfx 的**软件媒体后端** —— 用纯 Go 标准库可解码的 格式, 把 `<video>` / `<audio>` 从"契约 + 降级"变成"真的能播"。
x11
多显示器枚举 (factory 级 displayProvider 的 x11 实现, M4 2026-10-02)。
多显示器枚举 (factory 级 displayProvider 的 x11 实现, M4 2026-10-02)。
Package object 定义了 JavaScript 运行时中的所有值类型。
Package object 定义了 JavaScript 运行时中的所有值类型。
jsbuild 将 JavaScript 文件打包成独立的可执行程序。
jsbuild 将 JavaScript 文件打包成独立的可执行程序。
Package permissions 实现 Gox 的脚本权限模型(v1,Deno 风格)。
Package permissions 实现 Gox 的脚本权限模型(v1,Deno 风格)。
Package runtime 提供 JavaScript 运行时的基础组件: - Environment: 作用域环境链 (块作用域 + const 检查) - Iterator: 迭代器协议 (for...of)
Package runtime 提供 JavaScript 运行时的基础组件: - Environment: 作用域环境链 (块作用域 + const 检查) - Iterator: 迭代器协议 (for...of)
Package scaffold 是 Gox 自带的项目脚手架 —— 对标前端的 `npm create vite`: 一条命令铺出一个能直接跑起来的默认 GUI 工程。
Package scaffold 是 Gox 自带的项目脚手架 —— 对标前端的 `npm create vite`: 一条命令铺出一个能直接跑起来的默认 GUI 工程。
gx/crypto: 摘要与随机数的**最小可用集** (看板 ru3TZK)。
gx/crypto: 摘要与随机数的**最小可用集** (看板 ru3TZK)。
test
bench command
tools
bcdump command
bcdump 是调试工具: 编译一个 JS 源文件并反汇编主程序字节码。
bcdump 是调试工具: 编译一个 JS 源文件并反汇编主程序字节码。
certgen
Package certgen 为 `gox cert` 提供纯 Go 的证书生成能力。
Package certgen 为 `gox cert` 提供纯 Go 的证书生成能力。
cpu-prof command
cpu-prof: 临时性能剖面工具 — 跑一份 JS 脚本并输出 CPU profile。
cpu-prof: 临时性能剖面工具 — 跑一份 JS 脚本并输出 CPU profile。
icongen
Package icongen 负责"单源图标 + 平台资源"的全部纯 Go 生成逻辑:
Package icongen 负责"单源图标 + 平台资源"的全部纯 Go 生成逻辑:
initpurity command
Command initpurity 是「包级 init() 不得做平台 IO / 宏内核调用」的静态闸门。
Command initpurity 是「包级 init() 不得做平台 IO / 宏内核调用」的静态闸门。
memdrift
Package memdrift 是 GUI 常驻进程的长时内存漂移测试(T3-3)。
Package memdrift 是 GUI 常驻进程的长时内存漂移测试(T3-3)。
perf
Package perf 是 M9「并发与性能」的**可复现性能基准设施**。
Package perf 是 M9「并发与性能」的**可复现性能基准设施**。
propdump command
Command propdump 从 gfx/*.go 的**属性读取点**反推「内核认得哪些属性 / 事件」, 供 scripts/gen-props-golden.py 生成 docs/props.golden.json(看板 rgQsDD)。
Command propdump 从 gfx/*.go 的**属性读取点**反推「内核认得哪些属性 / 事件」, 供 scripts/gen-props-golden.py 生成 docs/props.golden.json(看板 rgQsDD)。
rssmon command
tools/rssmon —— GUI 常驻进程 RSS 曲线采样器(r95WjJ)。
tools/rssmon —— GUI 常驻进程 RSS 曲线采样器(r95WjJ)。
tstransform
Package tstransform 把 TypeScript / TSX 源码转译为等价的 JS 源码。
Package tstransform 把 TypeScript / TSX 源码转译为等价的 JS 源码。
update
Package update 实现 Gox 桌面应用的自动更新 (gx/update v1.1)。
Package update 实现 Gox 桌面应用的自动更新 (gx/update v1.1)。
Package vm 实现了 JavaScript 的栈式字节码虚拟机。
Package vm 实现了 JavaScript 的栈式字节码虚拟机。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL