A Next-Generation Compile-Time IoC & Reactive Domain Architecture for React & Vue.
Separate domain logic from UI components with Spring-like
@Autowired(), zero-boilerplate Vite AOT dependency injection, and hierarchical scoped containers.
- 🎯 100% 领域与 UI 解耦(Separation of Concerns):业务逻辑编写为纯粹的 TypeScript 类(Services),React 与 Vue 组件只负责纯渲染,业务核心 100% 无缝复用。
- 🍃 Spring 风格
@Autowired()属性注入:基于 TypeScript 装饰器与类型元数据,零样板参数、零显式 Token,像写 Java Spring Boot 一样自然优雅。 - 🌳 父子作用域与动态容器(Hierarchical Scoped DI):通过
rootContainer.createScope(item)轻松挂载局部领域对象(如多商品行、多图元实体),自动继承全局单例(汇率、税率、日志),支持精准生命周期销毁与内存释放(dispose())。 - ⚡ 编译期 AOT 静态扫描(Vite Plugin):
- 自动扫描全项目
@Service,零手动配置构建静态依赖图; - 构建期精确排查循环依赖、接口缺失与多实现歧义;
- 智能 Tree-shaking:基于入口文件进行可达性分析,未使用的 Service 自动剔除。
- 自动扫描全项目
- 🔌 接口与契约驱动(Interface-Based DI):支持基于 TypeScript
interface的多实现装配、@Qualifier()命名限定符以及多环境 Profile 切换(dev/prod/mock)。 - 🔄 双向细粒度响应式支持:
- React:内置极速 Proxy 属性级订阅,仅在消费的字段变化时精准局部重渲染;
- Vue 3:无缝桥接 Vue 3 响应式系统(
ref/computed/reactive)。
Separa 采用 Monorepo 架构,职责分明:
| 包名 | 职责描述 |
|---|---|
@separa/core |
核心装饰器(@Service, @Autowired, @Inject, @Qualifier)、Token 体系、响应式增强器 |
@separa/ioc-inversify |
现代 IoC 容器引擎、父子作用域(createScope)、动态模块与生命周期管理 |
@separa/plugin |
Vite 编译期 AOT 依赖分析插件、虚拟注册表生成、静态架构校验(同时支持 webpack / rspack / rollup / esbuild) |
@separa/react |
React 适配层(<SeparaProvider />, useService(), useContainer()) |
@separa/vue |
Vue 3 适配层(SeparaPlugin, useService(), useContainer()) |
@separa/taro |
Taro 小程序适配层(React 模式:import from "@separa/taro/react";Vue 模式:import from "@separa/taro/vue";useTaroPageScope() 自动管理页面子容器生命周期) |
@separa/uniapp |
uni-app 小程序适配层(Vue 3;useSeparaPageScope() / useSeparaComponentScope() 自动 dispose) |
@separa/miniprogram |
微信原生小程序适配层(defineSeparaPage() / defineSeparaComponent();Service 状态自动桥接至 setData()) |
// vite.config.ts import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; // 或 vue() import { separa } from "@separa/plugin"; export default defineConfig({ plugins: [ react(), separa({ // 可选:指定静态分析扫描入口与规则 include: ["src/**/*.ts", "src/**/*.tsx"], }), ], });
// services/currency.service.ts import { Autowired, Service } from "@separa/core"; import { LogService } from "./log.service"; @Service({ scope: "singleton" }) export class CurrencyService { currentCurrency: "CNY" | "USD" = "CNY"; // 🚀 Spring 风格自动装配:自动根据 TS 类型反射注入 LogService @Autowired() private logService!: LogService; setCurrency(currency: "CNY" | "USD") { this.currentCurrency = currency; this.logService?.log(`Currency changed to ${currency}`); } format(amountCNY: number): string { const rate = this.currentCurrency === "USD" ? 0.14 : 1; const symbol = this.currentCurrency === "USD" ? "$" : "\"; return `${symbol}${(amountCNY * rate).toFixed(2)}`; } }
// App.tsx import React from "react"; import { useService } from "@separa/react"; import { CurrencyService } from "./services/currency.service"; export function App() { const currency = useService(CurrencyService); return ( <div> <p>当前货币: {currency.currentCurrency}</p> <p>格式化金额: {currency.format(100)}</p> <button onClick={() => currency.setCurrency("USD")}>切换为 USD</button> </div> ); }
<!-- App.vue --> <script setup lang="ts"> import { useService } from "@separa/vue"; import { CurrencyService } from "./services/currency.service"; const currency = useService(CurrencyService); const { currentCurrency, format, setCurrency } = currency; </script> <template> <div> <p>当前货币: {{ currentCurrency }}</p> <p>格式化金额: {{ format(100) }}</p> <button @click="setCurrency('USD')">切换为 USD</button> </div> </template>
当构建复杂领域(例如电商购物车的每一行商品、绘图画板的每一个图形节点)时,每个子实体需要独立的生命周期与状态,同时又要共享根容器的全局服务(税率、汇率、审计日志)。
Separa 提供了极其直观的 Zero-Token 实例级 Scope 挂载:
// 1. 商品实体(普通领域类,仅接收业务数据) export class ItemService { id: string; price: number; quantity: number; @Autowired() private logService!: CartLogService; // 自动从父容器注入全局服务 constructor(props: { id: string; price: number; quantity: number }) { this.id = props.id; this.price = props.price; this.quantity = props.quantity; } } // 2. 算价引擎(动态注入当前行的 item,并从全局父容器继承 tax 和 currency) @Service({ scope: "transient" }) export class ItemCalculatorService { @Autowired() readonly item!: ItemService; @Autowired() readonly taxService!: TaxService; @Autowired() readonly currencyService!: CurrencyService; get total(): number { return this.item.price * this.item.quantity * (1 + this.taxService.rate); } } // 3. 创建子作用域(零 Token,零中括号,极简传参) const item = new ItemService({ id: "item-1", price: 8999, quantity: 1 }); const childContainer = rootContainer.createScope(item); // 4. 解析算价引擎(自动拼装局部 item 与全局 TaxService/CurrencyService) const calculator = childContainer.get(ItemCalculatorService); console.log(calculator.total); // 5. 销毁作用域(一键释放内存与响应式监听) await childContainer.dispose();
仓库内包含 3 组各具代表性的跨框架示例应用(每个业务领域均提供 React 与 Vue 1:1 对等实现):
| 示例工程 | 业务领域 | 核心亮点展示 |
|---|---|---|
examples/react-todoexamples/vue-todo |
TodoMVC 任务管理 | • 纯 TS Domain Store 驱动 • React 与 Vue 100% 共享业务与持久化代码 |
examples/react-ballcraftexamples/vue-ballcraft |
BallCraft 物理碰撞沙盒 | • 6 个多层级专业领域服务协同调度 • 60FPS 极速渲染与细粒度状态同步 |
examples/react-cartexamples/vue-cart |
高阶父子作用域电商购物车 | • 父子容器层级隔离(Hierarchical Scoped DI) • Spring 风格 @Autowired() 零参数依赖装配• 多币种换算、多税区计算与动态销毁回收 |
# 启动 TodoMVC (React: 5174, Vue: 5175) pnpm --filter @separa/example-react-todo dev pnpm --filter @separa/example-vue-todo dev # 启动 BallCraft 物理引擎 (React: 5176, Vue: 5177) pnpm --filter @separa/example-react-ballcraft dev pnpm --filter @separa/example-vue-ballcraft dev # 启动 Scoped 购物车 (React: 5178, Vue: 5179) pnpm --filter @separa/example-react-cart dev pnpm --filter @separa/example-vue-cart dev
# 安装依赖 pnpm install # 全量构建所有核心包与示例 pnpm build pnpm build:examples # 全量类型检查与自动化测试 (16 个测试套件,82 项单元测试) pnpm check
Separa 完整支持三大小程序平台,业务 Service 层代码 100% 共享,仅 UI 适配层不同。
pnpm add @separa/taro @separa/ioc-inversify @separa/plugin
config/index.ts(Taro webpack 构建配置):
import SeparaPlugin from "@separa/plugin/webpack"; export default { plugins: { webpackChain(chain: any) { chain.plugin("separa").use(SeparaPlugin, [{ include: ["src/**/*.ts"] }]); }, }, };
页面中使用:
import { useTaroPageScope, useService } from "@separa/taro/react"; import { SeparaProvider } from "@separa/taro/react"; import { rootContainer } from "@/container"; import { CartService } from "@/services/cart.service"; export default function CartPage() { const pageContainer = useTaroPageScope(rootContainer); return ( <SeparaProvider container={pageContainer}> <CartView /> </SeparaProvider> ); } function CartView() { const cart = useService(CartService); // 响应式,状态变更自动触发重渲染 return <View>合计:{cart.total}</View>; }
<script setup lang="ts"> import { useTaroPageScope, useService, provideContainer } from "@separa/taro/vue"; import { rootContainer } from "@/container"; import { CartService } from "@/services/cart.service"; const pageContainer = useTaroPageScope(rootContainer); provideContainer(pageContainer); const cart = useService(CartService); </script> <template> <view>合计:{{ cart.total }}</view> </template>
pnpm add @separa/uniapp @separa/ioc-inversify @separa/plugin
vite.config.ts:
import { separa } from "@separa/plugin"; export default defineConfig({ plugins: [uni(), separa()] });
页面中使用:
<script setup lang="ts"> import { useSeparaPageScope, useService, provideContainer } from "@separa/uniapp"; import { rootContainer } from "@/container"; import { CartService } from "@/services/cart.service"; const pageContainer = useSeparaPageScope(rootContainer); // 页面销毁时自动 dispose provideContainer(pageContainer); const cart = useService(CartService); </script>
# 安装后在微信开发者工具中点击「构建 npm」
npm add @separa/miniprogram @separa/ioc-inversify @separa/coreapp.ts(初始化根容器):
import "reflect-metadata"; import { SeparaContainer } from "@separa/ioc-inversify"; import { defineDecoratedService } from "@separa/core"; import { CartService } from "./services/cart.service"; // 无 AOT 构建插件时,手动声明 Service(需在 tsconfig 开启 emitDecoratorMetadata) export const rootContainer = new SeparaContainer({ definitions: [ defineDecoratedService(CartService, ["total", "items"]), ], }); App({ onLaunch() {} });
pages/cart/index.ts:
import { defineSeparaPage } from "@separa/miniprogram"; import { rootContainer } from "../../app"; import { CartService } from "../../services/cart.service"; defineSeparaPage(rootContainer, { inject: { cart: CartService }, // WXML 中可直接绑定 {{cart.total}} onLoad() { // this.$services.cart → CartService 实例 // this.data.cart → 自动与 Service 状态同步 }, });
pages/cart/index.wxml:
<view>合计:{{cart.total}}</view> <view wx:for="{{cart.items}}" wx:key="id">{{item.name}}</view> <button bindtap="checkout">结算</button>
tsconfig.json 必须配置:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }微信小程序基础库要求 ≥ 2.12.0(Proxy 支持)。