一直以来,Vue 到 React 的代码转换,要么靠手写重做,要么靠半成品的工具跑一半卡住。我做
VuReact就是想验证一件事:Vue 到 React 能不能在工程层面真正互通? 不是语法高亮,不是简单替换,而是从script setup到 JSX、从响应式到生命周期、从样式作用域到类型定义——能不能完整编译过去。下面是它的快速上手,你看完就知道我说的“完整验证”是什么意思。
什么是 VuReact?
VuReact(发音 /vjuːˈriːækt/)是一个面向 Vue 3 → React 的智能编译工具链。
它不是简单的语法转换,而是语义级编译:理解 Vue 代码的意图,生成符合 React 最佳实践的代码。由编译时转换 + 运行时适配两部分构成。
核心策略是 “约定优先” ——通过明确的编译约定,确保转换稳定可靠,尤其适合渐进式迁移场景。
快速开始
本节将引导你完成第一个 VuReact 项目的创建、编译和运行。
完成后你会明确三件事:
- 输入 SFC 在什么约定下可稳定转换
- 编译后目录会长什么样
- 输出 TSX 与原始 SFC 的语义对应关系
Step 0:准备目录
先准备一个最小工程(示意):
my-app/
├─ src/
│ ├─ components/
│ │ └─ Counter.vue
│ ├─ main.ts
│ └─ index.css
├─ package.json
└─ vureact.config.js
Step 1:安装
在你的 Vue 项目中安装 VuReact 编译器:
# 使用 npm
npm install -D @vureact/compiler-core
# 使用 yarn
yarn add -D @vureact/compiler-core
# 使用 pnpm
pnpm add -D @vureact/compiler-core
Step 2:编写输入 SFC
src/components/Counter.vue
<template>
<section class="counter-card">
<h2>{{ title }}</h2>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
<button @click="methods.decrease">-1</button>
</section>
</template>
<script setup lang="ts">
// @vr-name: Counter (注:用于告诉编译器,该生成什么组件名)
import { computed, ref } from 'vue';
// 也可以使用宏定义组件名
defineOptions({ name: 'Counter' });
const step = ref(1);
const count = ref(0);
const title = computed(() => `Counter x${step.value}`);
const increment = () => {
count.value += step.value;
};
const methods = {
decrease() {
count.value -= step.value;
},
};
</script>
<style lang="less" scoped>
@border-color: #ddd;
@border-radius: 8px;
@padding-base: 12px;
.counter-card {
border: 1px solid @border-color;
border-radius: @border-radius;
padding: @padding-base;
}
</style>
Step 3:配置编译器
vureact.config.js
import { defineConfig } from '@vureact/compiler-core';
export default defineConfig({
input: 'src',
// 关键:排除 Vue 入口文件,避免入口语义冲突
exclude: ['src/main.ts'],
output: {
workspace: '.vureact',
outDir: 'dist',
// 教程场景关闭环境初始化,便于观察纯编译产物
bootstrapVite: false,
},
format: {
enabled: true, // 开启格式化,同时这也会增加编译耗时。
formatter: 'prettier',
},
});
Step 4:执行编译
方式一:使用 npx 命令
在根目录下运行:
npx vureact build
方式二:使用 npm scripts
在 package.json 里添加脚本命令:
"scripts": {
"watch": "vureact watch",
"build": "vureact build"
}
npm run build
Step 5:查看输出目录树
编译后目录(示意):
my-app/
├─ .vureact/
│ ├─ cache/
│ │ └─ _metadata.json
│ └─ dist/
│ └─ src/
│ └─ components/
│ ├─ Counter.tsx
│ └─ Counter-<hash>.css
├─ src/
│ └─ ...
└─ vureact.config.js
Step 6:对照生成结果
下面是一个格式化后的典型输出(为说明做了轻微简化,实际哈希与属性名以本地产物为准):
import { memo, useCallback, useMemo } from 'react';
import { useComputed, useVRef } from '@vureact/runtime-core';
import './Counter-a1b2c3.css';
// 固定使用 memo 包裹组件
const Counter = memo(() => {
// ref/computed 转换成了对等的适配 API
const step = useVRef(1);
const count = useVRef(0);
const title = useComputed(() => `Counter x${step.value}`);
// 自动分析顶层箭头函数依赖,并追加 useCallback 优化
const increment = useCallback(() => {
count.value += step.value;
}, [count.value, step.value]);
// 自动分析顶层对象中的依赖,并追加 useMemo 优化
const methods = useMemo(
() => ({
decrease() {
count.value -= step.value;
},
}),
[count.value],
);
return (
<>
<section className="counter-card" data-css-a1b2c3>
<h2 data-css-a1b2c3>{title.value}</h2>
<p data-css-a1b2c3>Count: {count.value}</p>
<button onClick={increment} data-css-a1b2c3>
+1
</button>
<button onClick={methods.decrease} data-css-a1b2c3>
-1
</button>
</section>
</>
);
});
export default Counter;
CSS 文件内容:
.counter-card[data-css-a1b2c3] {
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
}
关键观察点
// @vr-name: Counter这段特殊注释定义了组件名- 非纯 UI 展示组件,默认会走
memo包装 ref/computed被转换为 runtime 适配 API(useVRef/useComputed)- 模板事件回调会生成符合 React 语义的
onClick - 顶层箭头函数依赖会尝试注入
useCallback - 顶层变量声明会尝试注入
useMemo - 对 JSX 中的原
ref状态值补上.value less样式被编译为 css 代码scoped样式会生成带哈希的 css 文件,并在元素上标注作用域属性
常见失败点
- 没排除 Vue 入口文件,如
src/main.ts或App.vue - 在非顶层调用会被转换为 Hook 的 API
- 模板里出现不可分析表达式并被告警
- 关闭样式预处理且使用
scoped,导致作用域失效
生态集成
- Vue 核心适配包:提供 React 版的 Vue 常用内置组件、核心 Composition API 等
- Vue 路由适配包:支持 Vue Router 4.x -> React Router DOM 7.9+ 转换
如果确实需要,你可以选择 ☣️混合编写,以此直接使用 React 生态。
🔗 链接
- GitHub:github.com/vureact-js/…
- 文档:vureact.top
- npm:www.npmjs.com/package/@vu…