VuReact 是一款 Vue 转 React 编译工具,它能将 Vue 3 代码编译为标准、可维护的纯 React 。
🌐 Github:github.com/vureact-js/…
📃 官方文档:vureact.top
📢 写在前面
本教程帮助开发者在最短时间内上手 VuReact,并完成一个入门 Vue 3 项目到 React 项目的编译转换并启动应用。你将掌握从环境搭建、组件编写、编译配置到产物验证的全流程,无需深入了解 React 语法细节,即可利用现有 Vue 技能生成可运行的 React 工程。
完成后你会明确以下四件事:
- 输入 SFC 在什么约定下可稳定转换
- 编译后目录会长什么样
- 输出 TSX 与原始 SFC 的语义对应关系
- 编译器自动分析并追加 React hook 依赖,无需手动管理
开始:新建 Vite + Vue 工程
- 使用
Vite新建一个标准的Vue 3+TS项目:
npx create-vite@latest vue-app --template vue-ts
-
当出现交互式选择
Install with npm and start now?时,选择No。 -
你将会看到类似以下工程目录结构(示意):
vue-app/
├─ public/
├─ src/
│ ├─ assets/
│ ├─ components/
│ │ └─ HelloWorld.vue
│ ├─ App.vue
│ ├─ main.ts
│ └─ style.css
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ ...
第1步:安装 VuReact
- 进入目录并安装项目依赖:
cd vue-app
npm install
- 安装 VuReact 编译核心:
npm install -D @vureact/compiler-core
第2步:配置 VuReact
在 vue-app 目录下新建 vureact.config.ts:
// vue-app/vureact.config.ts
import { defineConfig } from '@vureact/compiler-core';
export default defineConfig({
// 输入路径,包含要编译的 Vue 文件;允许输入单文件 'xxx.vue'
input: './src',
// 排除 Vue 入口文件,避免语义冲突
exclude: ['src/main.ts'],
output: {
// 工作区目录,存放编译产物和缓存
workspace: '.vureact',
// 输出目录名
outDir: 'react-app',
// 自动初始化 Vite React 环境
bootstrapVite: true,
},
});
除
exclude需手动指定外,其余选项均使用默认值,无需额外配置。
第3步:编写 Vue 组件
3.1 实现一个简单的计数器
将原来的 HelloWorld.vue 替换为计数器组件代码:
<!-- src/components/HelloWorld.vue -->
<template>
<section class="counter-card">
<h1>{{ props.title }}</h1>
<h2>
<span class="vureact">VuReact</span>
➕
<span class="vue">Vue</span>
🟰
<span class="react">React</span>
({{ count }})
</h2>
<p>{{ title }}</p>
<button @click="increment">+1</button>
<button @click="methods.decrease">-1</button>
</section>
</template>
<script setup lang="ts">
// @vr-name: HelloWorld
import { computed, ref, watch } from 'vue';
// 除了顶部的特殊注释外,也可以使用宏定义组件名
// defineOptions({ name: 'HelloWorld' });
// 必须使用 defineProps 定义 props
const props = defineProps<{ title?: string }>();
// 必须使用 defineEmits 定义 emits
const emits = defineEmits<{
(e: 'update', value: number): void;
}>();
const step = ref(1);
const count = ref(0);
const title = computed(() => `阶数:x${step.value}`);
const increment = () => {
count.value += step.value;
emits('update', count.value);
};
const methods = {
decrease() {
count.value -= step.value;
emits('update', count.value);
},
};
watch(count, (newVal) => {
step.value = Math.floor(newVal / 10) || 1;
});
</script>
<!-- VuReact 支持处理 Less 和 Sass -->
<style scoped>
.counter-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
.vureact {
color: #9932cc;
}
.vue {
color: #42b883;
}
.react {
color: #61dafb;
}
}
</style>
3.2 修改 App.vue
<!-- src/App.vue -->
<script setup lang="ts">
// @vr-name: App
import HelloWorld from './components/HelloWorld.vue';
</script>
<template>
<HelloWorld
title="计数器组件"
@update="(v) => {console.log(v)}"
/>
</template>
第4步:编译到 React 工程
方式一:使用 npx 命令
在 vue-app 目录下运行:
# 全量/增量编译
npx vureact build
# 或监听模式
npx vureact watch
方式二:使用 npm scripts
在 package.json 里添加脚本命令:
"scripts": {
"vr:build": "vureact build",
"vr:watch": "vureact watch"
}
npm run vr:build
运行命令后,终端会输出相关编译信息。
第5步:查看输出目录树
输出到 vue-app/.vureact 工作区目录(示意):
vue-app/
├── .vureact/ # 工作区(编译生成)
│ ├── cache/ # 编译缓存
│ ├── react-app/ # 生成的 Vite + React 工程
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── HelloWorld.tsx
│ │ │ │ └── HelloWorld-[hash].css
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── style.css
│ │ └── package.json
│ │ └── tsconfig.json
│ │ └── vite.config.ts
│ │ └── ...
│ │
├── src/ # 原始 Vue 代码
├── ...
└── vureact.config.js # VuReact 配置文件
第6步:运行 React 应用
- 进入
react-app目录:
cd .vureact/react-app
- 安装依赖:
npm run install
- 启动项目:
npm run dev
进入页面后,你可能会发现与 Vue 的页面样式存在差异,这是因为 Vite 初始化 React 后,自带的默认样式
index.css注入到了main.tsx中导致的,手动调整即可。
如遇问题,可查阅 常见问题 章节。
第7步:对照生成结果
下面是一个格式化后的典型输出(为说明做了轻微简化,实际哈希与属性名以本地产物为准):
import { useComputed, useVRef, useWatch } from '@vureact/runtime-core';
import { memo, useCallback, useMemo } from 'react';
import './HelloWorld-ebf8d8dc.css';
// VuReact 根据 defineProps 和 defineEmits 自动生成
export type IHelloWorldProps = {
title?: string;
} & {
onUpdate?: (value: number) => void;
};
// 自动使用 memo 优化组件
const HelloWorld = memo((props: IHelloWorldProps) => {
// ref/computed 转换成了对等的适配 API
const step = useVRef(1);
const count = useVRef(0);
const title = useComputed(() => `阶数:x${step.value}`);
// 自动分析顶层箭头函数依赖,并追加 useCallback 优化
const increment = useCallback(() => {
count.value += step.value;
props.onUpdate?.(count.value); // emits 转换
}, [count.value, step.value, props.onUpdate]);
// 自动分析顶层变量中的依赖,并追加 useMemo 优化
const methods = useMemo(
() => ({
decrease() {
count.value -= step.value;
props.onUpdate?.(count.value);
},
}),
[count.value, step.value, props.onUpdate],
);
// watch 转成对等适配 API
useWatch(count, (newVal) => {
step.value = Math.floor(newVal / 10) || 1;
});
return (
<>
<section className="counter-card" data-css-ebf8d8dc>
<h1 data-css-ebf8d8dc>{props.title}</h1>
<h2 data-css-ebf8d8dc>
<span class="vureact" data-css-ebf8d8dc>VuReact</span>
➕
<span class="vue" data-css-ebf8d8dc>Vue</span>
🟰
<span class="react" data-css-ebf8d8dc>React</span>
({count.value})
</h2>
{/* 自动补齐 ref .value 访问 */}
<p data-css-ebf8d8dc>{title.value}</p>
<button onClick={increment} data-css-ebf8d8dc>
+1
</button>
<button onClick={methods.decrease} data-css-ebf8d8dc>
-1
</button>
</section>
</>
);
});
// 自动保持组件导出
export default Counter;
CSS 文件内容:
.counter-card[data-css-ebf8d8dc] {
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
background: #fafafa;
.vureact[data-css-ebf8d8dc] {
color: #9932cc;
}
.vue[data-css-ebf8d8dc] {
color: #42b883;
}
.react[data-css-ebf8d8dc] {
color: #61dafb;
}
}
关键观察点
// @vr-name: Counter这段特殊注释定义了组件名defineProps和defineEmits被转换成了 TS 组件类型- 非纯 UI 展示组件,默认会走
memo包装 ref/computed/watch被转换为 runtime 适配 API(useVRef/useComputed/useWatch)- 模板事件回调会生成符合 React 语义的
onClick - 顶层箭头函数自动分析依赖,尝试注入
useCallback - 顶层变量声明自动分析依赖,尝试注入
useMemo - 对 JSX 中的原
ref/computed状态值补上.value scoped样式会生成带哈希的 css 文件,并在元素上标注作用域属性
总结
通过以上步骤,你已完成了一个 Vue SFC 项目到 React 项目的完整编译流程。回顾整个过程:
- 初始化项目:使用 Vite 创建标准的 Vue + TS 工程
- 安装编译器:添加
@vureact/compiler-core依赖 - 编写配置:通过
vureact.config.ts指定输入、排除和输出规则 - 编写组件:按照约定(
@vr-name注释、defineProps/defineEmits宏)编写 SFC - 执行编译:使用 CLI 命令一键转换
- 运行产物:直接启动生成的 React 工程并验证效果
VuReact 在编译过程中拥有以下核心转换能力:
- Vue 模板语法 → React JSX(
v-if/v-slot/v-model/<slot>等) - Composition API → React Hooks(
ref→useVRef、computed→useComputed) - 响应式依赖分析 → 自动注入
useCallback/useMemo依赖数组 - 组件通信 → Props 类型推导 + 事件回调映射
- 样式处理 → Scoped CSS / CSS Modules / 预处理器一站式编译
常见问题
对于使用过程中的常见问题,可参考以下文档:
✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!Github 仓库点亮 Star ⭐!