多框架组件库跨端编译流水线:一套核心产出 React/Vue3/Svelte 产物
·
多框架组件库跨端编译流水线:一套核心产出 React/Vue3/Svelte 产物

在现代大型企业级前端体系中,“技术栈的多元并存”是一个无法回避的客观事实:
- 核心云平台中后台业务线全面使用 React 18;
- 移动端 H5 营销与活动系统广泛使用 Vue 3 (Composition API);
- 高性能轻量级桌面客户端或前沿插件选择基于 Svelte 5 构建;
- 微前端架构中部分团队需要纯原生的 Web Components (Custom Elements)。
面对这种技术栈割裂,如果设计系统团队采用“针对每个框架各安排一组工程师、手工独立维护一套组件库”的原始做法,会导致灾难性的后果:
- 人力成本翻 4 倍:同一个
<Modal>弹窗或<DatePicker>,要在四个不同的仓库里重复手写 4 次; - 视觉与逻辑严重脱节:React 团队修复了一个无障碍键盘 Bug,Vue 团队由于排期延后了三个月,Svelte 团队根本没人维护,导致全公司的统一设计规范名存实亡!
“单一真实源(Single Source of Truth)的跨框架组件编译器(Cross-Framework Component Compiler / 如 Mitosis 架构)” 是解决多框架组件库大一统的终极工程解法。
本文将深入拆解这套“一套声明式核心代码,编译产出全框架原生组件”的工业级流水线。
跨框架组件编译器的核心抽象架构
[统一声明式抽象源码: Button.lite.tsx (Mitosis JSX State Model)]
│
▼ (Babel / TypeScript AST 抽象语义解析)
┌─────────────────────────────┼─────────────────────────────┬─────────────────────────────┐
▼ ▼ ▼ ▼
[React 目标生成器] [Vue 3 目标生成器] [Svelte 目标生成器] [Web Components]
- React.FC / JSX - <script setup lang="ts"> - Svelte 5 Runes 语法 - 自定义元素 CustomElement
- Hooks (useState, useEffect) - ref / computed 响应式 - $state / $derived - Shadow DOM 样式隔离
│
▼
[自动发布为 @company/ui-react, @company/ui-vue, @company/ui-svelte 独立 npm 包!]
1. 编写框架无关的抽象声明式组件(.lite.tsx)
在抽象源码中,我们使用被约束的声明式子集编写组件的状态与视图,不依赖任何特定框架的运行时:
// src/components/BadgeButton/BadgeButton.lite.tsx
import { useStore, Show } from '@builder.io/mitosis';
export interface BadgeButtonProps {
label: string;
count?: number;
variant?: 'primary' | 'secondary';
onClick?: () => void;
}
export default function BadgeButton(props: BadgeButtonProps) {
const state = useStore({
isHovered: false,
setHovered(val: boolean) {
state.isHovered = val;
},
});
return (
<button
type="button"
onClick={() => props.onClick?.()}
onMouseEnter={() => state.setHovered(true)}
onMouseLeave={() => state.setHovered(false)}
className={`relative inline-flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all ${
props.variant === 'secondary'
? 'bg-slate-800 text-slate-200 border border-slate-700'
: 'bg-indigo-600 text-white shadow-lg shadow-indigo-600/30'
}`}
>
<span>{props.label}</span>
{/* 条件渲染声明 */}
<Show when={Boolean(props.count && props.count > 0)}>
<span className="px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-rose-500 text-white">
{props.count}
</span>
</Show>
</button>
);
}
2. 自动化编译构建脚本(Node.js + Mitosis CLI)
编写构建流水线脚本,将 .lite.tsx 批量翻译为各个框架的地道生产代码:
// scripts/compile-cross-framework.ts
import * as fs from 'fs';
import * as path from 'path';
import { parseJsx } from '@builder.io/mitosis';
import { componentToReact } from '@builder.io/mitosis';
import { componentToVue } from '@builder.io/mitosis';
import { componentToSvelte } from '@builder.io/mitosis';
import { globSync } from 'glob';
export async function buildAllFrameworks() {
const sourceFiles = globSync('src/components/**/*.lite.tsx');
for (const file of sourceFiles) {
const rawCode = fs.readFileSync(file, 'utf8');
const componentJson = parseJsx(rawCode);
const baseName = path.basename(file, '.lite.tsx');
// 1. 编译为原生 React TSX
const reactOutput = componentToReact({ typescript: true })(componentJson);
fs.writeFileSync(`packages/react/src/${baseName}.tsx`, reactOutput, 'utf8');
// 2. 编译为原生 Vue 3 SFC (<script setup>)
const vueOutput = componentToVue({ api: 'composition', typescript: true })(componentJson);
fs.writeFileSync(`packages/vue/src/${baseName}.vue`, vueOutput, 'utf8');
// 3. 编译为原生 Svelte 5
const svelteOutput = componentToSvelte({ typescript: true })(componentJson);
fs.writeFileSync(`packages/svelte/src/${baseName}.svelte`, svelteOutput, 'utf8');
console.log(`✅ 成功跨端编译组件: [${baseName}] ➔ React / Vue / Svelte`);
}
}
3. 自动生成的各框架真实产物对比
生成的 Vue 3 产物(地道 Composition API)
<!-- packages/vue/src/BadgeButton.vue -->
<template>
<button
type="button"
@click="props.onClick?.()"
@mouseenter="state.setHovered(true)"
@mouseleave="state.setHovered(false)"
:class="`relative inline-flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all ${
props.variant === 'secondary'
? 'bg-slate-800 text-slate-200 border border-slate-700'
: 'bg-indigo-600 text-white shadow-lg shadow-indigo-600/30'
}`"
>
<span>{{ props.label }}</span>
<span v-if="Boolean(props.count && props.count > 0)" class="px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-rose-500 text-white">
{{ props.count }}
</span>
</button>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const props = defineProps<{
label: string;
count?: number;
variant?: 'primary' | 'secondary';
onClick?: () => void;
}>();
const state = reactive({
isHovered: false,
setHovered(val: boolean) {
state.isHovered = val;
}
});
</script>
生成的 Svelte 产物(地道轻量语法)
<!-- packages/svelte/src/BadgeButton.svelte -->
<script lang="ts">
export let label: string;
export let count: number = 0;
export let variant: 'primary' | 'secondary' = 'primary';
export let onClick: (() => void) | undefined = undefined;
let isHovered = false;
</script>
<button
type="button"
on:click={() => onClick && onClick()}
on:mouseenter={() => isHovered = true}
on:mouseleave={() => isHovered = false}
class="relative inline-flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all {variant === 'secondary' ? 'bg-slate-800 text-slate-200 border border-slate-700' : 'bg-indigo-600 text-white shadow-lg shadow-indigo-600/30'}"
>
<span>{label}</span>
{#if count && count > 0}
<span class="px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-rose-500 text-white">
{count}
</span>
{/if}
</button>
总结
跨框架组件库的编译流水线是现代大型前端基础设施最高阶的生产力放大器。将组件逻辑抽象为框架无关的 AST 语法树,在构建期全自动翻译为各前端生态最地道、零额外运行时开销的原生包,我们用 1 份代码的研发与维护成本,完美征服了 4 大前端生态,让企业级设计系统的统一视觉契约真正无远弗届。
更多推荐



所有评论(0)