tsrx-org/tsrx · 上手攻略

  • 仓库:tsrx-org/tsrx
  • 链接:https://github.com/tsrx-org/tsrx
  • 分类:前端工程 · 语言扩展 · 编译工具链
  • 作者:Tom
  • 更新:2026-08-28

是什么

TSRX(TypeScript Render Extensions)是一个 TypeScript 语言扩展,用于构建声明式 UI。它将 TypeScript 设置逻辑、JSX 形态的结构、模板控制流和作用域样式统一写在 .tsrx 文件中,再编译为所选运行时的惯用输出。

核心设计哲学:同一种语言,多种运行时。TSRX 先将源码解析为框架无关的 AST,再由目标编译器(如 @tsrx/react@tsrx/solid)生成对应框架的代码——React、Preact、Solid、Vue、Ripple、Octane 六种目标,当前均已支持。语法上借鉴 JSX 但有自己独特的语法糖(@{ ... } 语句容器、&{ ... } 惰性解构、@if/@for/@switch/@try 模板控制流),由 @tsrx/typescript-plugin 提供编辑器级的类型检查与导航。

作者是 Dominic Gannaway(React 团队早期成员、React 18 concurrent mode 关键贡献者),MIT 许可证,当前处于活跃 beta 开发阶段。


解决什么问题

1. JSX 表达力受限

JSX 把控制流挤压到表达式槽位,三元嵌套、map 链、render helper 满天飞。TSRX 把控制流做成模板级一等语法(@if/@for/@switch/@try),写在模板里而不是 JavaScript 表达式里。

2. 样式与结构分离

传统方案:CSS 文件或 CSS-in-JS 与组件分离。TSRX 支持在同一个 .tsrx 文件里写 <style> 块,编译时自动做 class hashing(类似 CSS Modules),作用域封闭而不污染全局。

3. 组件局部状态与渲染结果割裂

React Hooks 要求把状态逻辑抽到函数顶层,逻辑与渲染目标物理分离。TSRX 的 @{ ... } 语句容器把"局部变量/派生值/副作用"和"最终输出节点"放在同一个词法作用域,代码共置更紧凑。

4. 多框架重复造轮子

每个框架都有自己的 JSX 方言、组件写法、样式方案。TSRX 用同一套语言写出组件,换目标框架只需要换编译插件,不需要重写组件代码。

5. AI 辅助编码友好

文档明确指出 TSRX 面向"AI 辅助编程时代"——代码结构显式(控制流非嵌套表达式)、共置(逻辑紧邻渲染目标)、无隐式 scope,LLM 更容易理解和生成。


快速安装(React + Vite)

# 1. 安装核心包和 Vite 插件
pnpm install @tsrx/react @tsrx/vite-plugin-react

# 2. 在 vite.config.ts 中注册插件
# import { defineConfig } from 'vite';
# import tsrxReact from '@tsrx/vite-plugin-react';
# export default defineConfig({ plugins: [tsrxReact()] });

# 3. 创建 .tsrx 文件,从 TSX 中正常 import
# import { Counter } from './Counter.tsrx';
# 即可使用

支持的目标 + 构建工具组合(截至 2026-08,基于官方文档):

目标 构建工具 安装命令摘要
React Vite @tsrx/react + @tsrx/vite-plugin-react
React Rspack @tsrx/react + @tsrx/rspack-plugin-react
React Turbopack(Next.js) @tsrx/react + @tsrx/turbopack-plugin-react
React Bun @tsrx/react + @tsrx/bun-plugin-react
Preact Vite / Rspack / Bun @tsrx/preact + 对应插件
Solid Vite @tsrx/solid + @tsrx/vite-plugin-solid
Vue Vite @tsrx/vue + @tsrx/vite-plugin-vue(依赖 vue-jsx-vapor

⚠️ 注意:npm registry 上 @tsrx/core 最新版本 0.1.60(第三方来源,可能非官方);@tsrx/typescript-plugin 版本 0.3.124;具体版本号建议以 npm view @tsrx/react versions 实际查询为准,下方不再逐一标注。


核心用法

语句容器 @{ ... }

export function Cart({ items }: { items: Item[] }) @{
  const subtotal = items.reduce((sum, item) => sum + item.price, 0);
  const discount = subtotal > 100 ? 0.1 : 0;

  <div>
    <h2>Your cart</h2>
    <p>Subtotal: ${subtotal}</p>
    <p>Save: ${(subtotal * discount).toFixed(2)}</p>
  </div>
}

@{ 前面是 TypeScript 设置逻辑,} 后必须跟一个输出节点。设置逻辑与渲染结果共置,阅读时数据流方向清晰。

惰性解构 &{ ... }

// 普通解构在 Solid/Vue 等响应式运行时下会破坏响应性
// &{ ... } 编译时生成惰性 getter,运行时按需读取
function UserCard(&{ name, age }: { name: string; age: number }) {
  return <div>
    <h2>{name}</h2>
    <p>Age: {age}</p>
  </div>;
}

&{ ... } 对象解构和 &[ ... ] 数组解构编译到目标框架时,在 Solid/Vue/Ripple 下保留响应性追踪;在 React/Preact 下退化为直接属性访问,零开销。

模板控制流

// @if / @else if / @else
function StatusBadge({ status }: { status: 'active' | 'idle' | 'offline' }) @{
  @if (status === 'active') {
    <span class="badge active">Online</span>
  } @else if (status === 'idle') {
    <span class="badge idle">Away</span>
  } @else {
    <span class="badge">Offline</span>
  }
}

// @for (... of ...) with index and key
function TodoList({ items }: { items: Todo[] }) @{
  <ul>
    @for (const item of items; index i; key item.id) {
      <li>{i + 1}. {item.text}</li>
    } @empty {
      <li>No todos yet</li>
    }
  </ul>
}

// @switch
function StatusMessage({ status }: { status: string }) @{
  @switch (status) {
    @case 'loading': { <p>Loading...</p> }
    @case 'success': { <p class="success">Done!</p> }
    @default: { <p>Unknown status.</p> }
  }
}

// @try / @catch(错误边界)
function ErrorBoundaryWrapper() @{
  @try {
    <RiskyComponent />
  } @catch (error) {
    <FallbackUI error={error} />
  }
}

⚠️ 注意@if@for@switch@try 的分支 body 是模板级控制流,不是普通 JavaScript {} 块。普通函数体的 {} 内写 JSX 会触发编译错误——必须显式用 @{ ... } 包裹。

作用域样式 <style>

export function Button({ label, onClick }: {
  label: string;
  onClick: () => void;
}) @{
  <>
    <button class="btn" {onClick}>{label}</button>

    <style>
      .btn {
        padding: 0.5rem 1rem;
        border-radius: 4px;
      }
    </style>
  </>
}

<style> 块编译时自动 hash 类名,作用域封闭,不污染全局 CSS。编译产物为标准 CSS(或 CSS Modules,取决于构建工具配置)。

Prop 简写 {name} 代替 name={name}

// 两者等价:
<Input value={value} onChange={onChange} />
<Input {value} {onChange} />

当 prop 名与变量名一致时可用简写,包括事件处理器。


典型适用场景

  1. AI 辅助前端开发项目:TSRX 代码结构显式、逻辑与渲染共置,LLM 生成的可读性和可维护性高于手写复杂 JSX。文档明确以此为卖点。

  2. 多框架组件库:同一套组件源码需要同时支持 React、Preact、Solid、Vue 等多个目标,避免维护多套代码。⚠️ 注意:各目标框架的响应式模型不同(Solid/Vue 基于 Proxy/Signal,React 基于 Virtual DOM),复杂响应式逻辑在跨目标时仍需针对性调整。

  3. 中型企业内部工具 / 管理后台:样式共置减少文件切换;@if/@for 减少三元和 map 嵌套; scoped style 省去 CSS 架构决策(Tailwind / CSS Modules / Styled-Components)。

  4. TypeScript 重度项目:已有 TSX 基础设施,渐进式引入 .tsrx 文件,与现有 JS/TS/TSX 文件互操作,无需整体迁移。

  5. 声明式优先的 SSR 场景:Vue / Solid 目标支持服务端渲染,对需要首屏性能的页面有优势。


坑与注意

  1. beta 状态,API 可能 breaking change:官方标注"active beta development",生产项目引入需锁定版本号并关注 changelog。建议不要在核心业务线上直接依赖最新版,建议至少隔一个 minor 版本滞后升级。

  2. @{ ... } 语法与普通 { } 易混淆:在 .tsrx 文件里,普通 {} 是 JavaScript 表达式(不允许写 JSX);只有 @{ ... } 才能写 JSX。VS Code 插件和 TypeScript 插件会提示,但初期容易踩坑。

  3. Solid/Vue 等响应式目标的行为边界:惰性解构 &{ ... } 在 Solid/Vue 下保留响应性,但如果你在组件内直接做对象属性读写而不通过解构,响应性仍需手动处理。文档对这一边界说明较充分,建议先读目标框架的 TSRX 专有文档。

  4. <style> 块仅限组件级别:TSRX 的 <style> 块是组件级别的 scoped CSS,不支持跨组件共享样式(如 CSS custom properties 方案可以,但 <style> 块本身不会生成全局规则)。

  5. @for 循环体不是普通 JavaScript scope@for (... of ...) { ... } 循环体是模板渲染块,不允许 continue/break/return 等跳转语句。嵌套函数内可以用普通 JavaScript 控制流,但不能替代 @for 的渲染语义。

  6. 编译产物是 TSX/JS,而非运行时依赖:TSRX 是编译时工具,最终产物是目标框架的 TSX/JS 文件,运行时仍需要对应框架(React / Preact / Solid / Vue)。它不替代 React/Vue 等运行时

  7. @tsrx/typescript-plugin 版本跳号(来源:npm search,@tsrx/typescript-plugin v0.3.124 vs @tsrx/core v0.1.60):不同 @tsrx/* 包可能版本独立演进,安装时不要假设版本对齐,建议逐包 npm view @tsrx/<pkg> versions 核实。

  8. Vue 目标依赖 vue-jsx-vapor:Vue 目标需要额外安装 vue-jsx-vapor,不是原生 Vue SFC(Single File Component)——这意味着 TSRX Vue 组件形态与标准 .vue 文件不同,是 TSX 风格,对于习惯 Vue SFC 的团队有一定学习成本。


与同类对比

特性 TSRX Solid JSX React JSX Vue SFC
控制流语法 @if/@for 模板级 普通 JS 表达式 普通 JS 表达式 指令(v-if/v-for
样式共置 <style> CSS-in-JS / 外置 CSS-in-JS / 外置 <style> SFC 内
多框架目标 ✅ 6 种 ❌ 仅 Solid ❌ 仅 React ❌ 仅 Vue
语句容器共置 @{ ... }
惰性解构 &{ ... } 内联 computed computed()
编辑器插件 ✅ VS Code/Zed/Neovim
生产可用性 beta 稳定 稳定 稳定
AI 友好度 高(显式结构)

核心差异:TSRX 是唯一把"多框架编译目标 + 模板级控制流 + 语句容器共置 + 作用域样式"打包成一门语言的项目。SolidJS 放弃了 JSX 表达式嵌套但不支持多目标;Vue SFC 样式共置优秀但多框架为零;React JSX 生态最成熟但控制流嵌套问题无语言层解法。


一句话推荐结论

如果你的项目需要同时面向多个 UI 运行时(React / Solid / Vue / Preact),或者你在构建 AI 辅助编码友好的组件库,TSRX 是目前唯一在语言层做跨框架声明式 UI 的方案,值得关注其 beta 进展;如果是单一框架的已有项目,迁移成本需谨慎评估。


最小可跑命令

# 环境:Node.js ≥ 18,pnpm ≥ 8,TypeScript ≥ 5.0
# 硬件:无特殊要求,纯前端编译工具

pnpm create vite my-tsrx-app --template react-ts
cd my-tsrx-app
pnpm install @tsrx/react @tsrx/vite-plugin-react

# vite.config.ts 添加:
# import tsrxReact from '@tsrx/vite-plugin-react';
# export default defineConfig({ plugins: [tsrxReact()] });

# 创建 src/Counter.tsrx:
# export function Counter() @{
#   let count = 0;
#   <button onClick={() => count++}>Count: {count}</button>
# }

pnpm dev

⚠️ 最小可跑依赖实际安装 @tsrx/react + 对应构建插件版本;具体版本号以 npm view @tsrx/react versions 查询结果为准(web_fetch 无法读取完整 npm 版本列表)。


原始来源

  • GitHub 仓库:https://github.com/tsrx-org/tsrx(README.md + 仓库描述)
  • 官方文档:https://tsrx.dev/(Getting Started + Features 页面)
  • InfoQ 报道:https://www.infoq.com/news/2026/06/tsrx-alternative-jsx(2026-06-19)
  • npm:@tsrx/core v0.1.60 / @tsrx/typescript-plugin v0.3.124(npm search 结果,版本号 ⚠️ 待 npm view 核实)