v0.6零 Node零 JS 引擎零 npm

借 React 的思想,
编译到 Go 的全栈框架

一份 TSX 源码, 两个编译器: 服务端编成 Go 函数, 客户端编成 signals。没有 vdom, 没有 JS 引擎, 没有 Node。工具链只有 Go。

开始使用它是怎么工作的← 这些按钮是方言写的组件库
这个按钮是一个岛: Go 渲染了它, 浏览器 hydrate 后它是一个 signal。点它。
15 µs
50 件商品的列表页在 Go 里渲染完(进程内)
20k req/s
4 vCPU 的 GitHub runner, 64 连接 —— 与 templ 持平, 约 Next.js 的 65 倍
21 MB
负载下的峰值内存(Next.js: 387 MB)
9 KB
首屏 JS, gzip 后(运行时 + 加载器 + 岛)

一份源码, 两个编译器

.server.tsx 只编成 Go。.client.tsx 同时编成 Go(单趟求值做 SSR)和 JS(signals 做交互)。切换标签看同一份源码变成什么。

服务端页面 → Go
import type { PageProps } from "gotsx";
import { models } from "host:data";        // Go-backed, zero marshalling
import Layout from "../components/Layout.server";
import ModelCard from "../components/ModelCard.server";

export default function Home({ query }: PageProps) {
  const q = query.q ?? "";
  const list = models.search(q);           // synchronous: concurrency from goroutines
  return (
    <Layout title="Products">
      <div class="grid">{list.map((m) => <ModelCard model={m} />)}</div>
      {list.length === 0 && <p class="empty">No matching products</p>}
    </Layout>
  );
}
岛 → Go + JS
import { useState, useEffect } from "gotsx";

export default function Counter({ start }: { start: number }) {
  const [n, setN] = useState(start);
  const double = n * 2;                    // depends on n → auto memo
  useEffect(() => { console.log(n); });    // JS backend only
  return (
    <button onClick={() => setN(n + 1)}>
      {n} ×2 = {double}{n > 4 && <b> 🔥</b>}
    </button>
  );
}
为什么 TSX 能编成 Go
SSR 是一次同步的单趟求值: 没有重渲染、没有 effect、setter 永远不会被调用。所以服务端不需要"React 运行时", 只需要组件的"渲染切片"——而这个切片的语义小到可以编译成 Go。

特性

编译到 Go, 不是解释执行

服务端组件是真正的 Go 函数: JSX 编成字符串直写, useState 是初值, useEffect 什么都不生成。一个页面几十微秒。

signals 客户端

同一份 .client.tsx 编成 signals + 精确 DOM 绑定(Solid / Svelte 5 的模型), 没有 vdom, 运行时 6KB。

走位 hydrate

服务端只在响应式的文本/条件/列表上留标记, 客户端按同一编译器给出的结构顺序认领节点, 不需要 diff, 复用现有 DOM。

宿主模块

import { models } from "host:data" 背后是 Go, 编译后是直接调用, 零编组; 类型由 Go 反射生成。

岛 + SPA 跳转

页面零 JS, 岛按需加载; 跳转 = 拉 HTML → morph, 岛按 DOM 同一性存活, 状态不丢。

围栏是编译错误

客户端碰 host:*、缺 prop、any 上取成员、用了 ==……全部是带文件:行:列的错误, 不会静默编错。

Tailwind

class 就是字符串, Tailwind standalone CLI 构建期扫描 .tsx 生成 CSS, 同样不需要 Node。这个站就是。

gotsx dev + LSP

改 TSX → 重新编译 → go build → 重启约 2 秒, 浏览器自动刷新; 编译失败时旧版本继续跑。gotsx lsp 把同样的错误实时带进 VS Code / Neovim / Helix / Zed。

它不是什么

不是 React

没有 React 运行时, 不能用 npm 上的 React 组件库。借的是思想和语法手感, 语义是静态的。

不是 TypeScript

是一门借 TSX 语法的静态语言(AssemblyScript 的同类): 类型系统限定在 Go 能表示的集合里, 出了子集是编译错误。

不是 Next

没有 RSC 协议、没有 server actions、没有客户端数据获取框架。流式渲染是一个在 goroutine 里求值的 <Suspense> 边界; 有的是 Go 的速度、单二进制和 6KB 客户端。

常见问题

五分钟跑起来