大型 Vue 项目要迁移 React,传统路径只有两条:逐文件手工重写,或者引入双框架运行时做桥接。前者耗人耗时,后者让产物体积膨胀、调试体验割裂。VuReact 选了第三条路——纯 AST 编译,把 Vue 单文件组件(SFC)直接翻译成 React 组件,不附带任何额外运行时,产物体积零增长。1.8.4 版本集中修复了编译稳定性问题,意味着这条路从"能跑 demo"进入了"能扛工程"的阶段。
编译器做了什么
VuReact 的核心流程分三步:
- 解析 SFC——用 Vue 官方
@vue/compiler-sfc把.vue文件拆成 template / script / style 三块 AST。 - 模板转 JSX——将 Vue 模板指令(
v-if、v-for、v-model、v-on等)逐条映射为 React 表达式:v-if→ 三元运算符,v-for→Array.map,v-model→ 受控组件的value+onChange。 - 脚本转写——把 Vue 的响应式 API(
ref、computed、watch、onMounted等)替换为 React Hooks 等价实现:ref→useState,computed→useMemo,watch→useEffect,onMounted→useEffect(() => {}, [])。
style 块的处理相对独立: scoped CSS 通过自动生成唯一类名前缀实现等效隔离,普通 CSS 直接提取为独立样式文件。
关键点在于:整个过程发生在编译阶段,产物是纯 React 代码,运行时不需要 Vue 依赖,也不需要 VuReact 自己的运行时库。
1.8.4 稳定性修了哪些坑
早期版本在实际项目上暴露的问题集中在几个方向,1.8.4 针对性做了修补:
- 嵌套
v-for+v-if组合:以前遇到同一节点上既有v-for又有v-if,编译顺序会出错,导致条件判断跑到循环外面。1.8.4 修正了优先级,保证v-for在外层、v-if在内层,与 Vue 官方行为一致。 - 动态组件
<component :is>:此前直接映射为React.createElement(variable),没有处理字符串标签和组件引用的区分。现在会根据绑定值的类型分别生成<div>或<Component />。 v-model自定义参数:Vue 3 支持在组件上写v-model:title="x",旧版只处理了默认v-model,新版能正确编译为title={x} onUpdate:title={...}。- Slots 转 props.children:作用域插槽(
v-slot)的编译此前在多级嵌套场景下会丢失作用域变量,1.8.4 重写了 slots 的 AST 遍历逻辑。
这些修复让编译器在面对真实业务组件时不再频繁报错或产出错误 JSX,是"能用"到"可靠"的分界线。
实操:编译一个 Vue 组件
下面用一个完整的例子演示 VuReact 的使用流程。先准备一个典型 Vue 组件,然后用 CLI 编译,观察产物。
源文件 TodoItem.vue:
<template>
<li :class="{ done: item.completed }">
<input type="checkbox" v-model="item.completed" />
<span>{{ item.text }}</span>
<button @click="$emit('remove', item.id)">删除</button>
</li>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
item: { type: Object, required: true }
})
const emit = defineEmits(['remove'])
</script>
<style scoped>
.done span {
text-decoration: line-through;
color: #999;
}
</style>
安装 VuReact CLI 并编译:
# 安装
npm install -g @vureact/cli@1.8.4
# 单文件编译
vureact compile TodoItem.vue --outdir ./output
# 整个项目批量编译
vureact compile ./src/**/*.vue --outdir ./src-react
编译产物 TodoItem.jsx:
import React, { useState, useCallback } from 'react';
import './TodoItem.css'; // scoped CSS 提取为独立文件,类名加前缀
export default function TodoItem({ item, onRemove }) {
// v-model="item.completed" → 受控 checkbox
const [completed, setCompleted] = useState(item.completed);
const handleRemove = useCallback(() => {
onRemove(item.id);
}, [item.id, onRemove]);
return (
<li className={completed ? 'done _todoitem_abc123' : '_todoitem_abc123'}>
<input
type="checkbox"
checked={completed}
onChange={(e) => setCompleted(e.target.checked)}
/>
<span>{item.text}</span>
<button onClick={handleRemove}>删除</button>
</li>
);
}
可以看到几个关键映射:
defineProps→ 函数组件参数解构defineEmits(['remove'])→onRemovepropv-model→checked+onChange受控模式:class条件对象 →className三元表达式- scoped CSS → 独立
.css文件 + 自动类名前缀_todoitem_abc123
产物直接放进 React 项目即可使用,无需任何适配层。
批量迁移的工程策略
单文件编译验证通过后,面对整个项目需要一套流程:
# 1. 编译全部 Vue 组件到临时目录
vureact compile ./src/**/*.vue --outdir ./src-react --preserve-structure
# 2. 检查编译报告(1.8.4 新增 --report 输出)
vureact compile ./src/**/*.vue --outdir ./src-react --report ./migration-report.json
# 3. 对报告标记为 "manual-review" 的文件逐个处理
# 常见原因:render 函数组件、复杂动态 slot、自定义指令
--preserve-structure 保持原目录结构,--report 输出 JSON 格式的迁移报告,列出每个文件的编译状态和需要人工复查的原因。这比盲目编译后再排查问题高效得多。
迁移报告示例片段:
{
"files": [
{ "path": "src/components/TodoItem.vue", "status": "success", "warnings": [] },
{ "path": "src/views/Dashboard.vue", "status": "manual-review", "reasons": ["custom-directive:v-permission"] },
{ "path": "src/components/RichTable.vue", "status": "success", "warnings": ["nested-v-for-v-if-combo"] }
],
"summary": { "total": 47, "success": 43, "manualReview": 4 }
}
43 个文件自动通过,4 个需要人工介入——这个比例在真实项目中已经能省下大量重写工作。
边界与取舍
VuReact 不是万能钥匙,有几条硬边界需要提前知道:
- Vue 自定义指令(如
v-permission、v-loading)没有 React 等价物,编译器会标记为manual-review,需要手动用 React HOC 或 Hook 替代。 provide/inject跨层级依赖注入:编译器能转写为 React Context,但多层嵌套的 inject 默认值和响应式更新行为需要逐个验证。- Vue Router / Vuex / Pinia:这些框架级库的 API 不在组件编译范围内,需要单独规划路由和状态管理的迁移方案。
- 模板中的
$refs:编译为useRef,但模板 ref 和 React ref 的挂载时机有细微差异,涉及 DOM 操作的场景要测试。
务实做法是:先用 --report 扫一遍全量文件,把自动通过的文件直接采用,把 manual-review 的文件分批人工处理。不要试图一次性 100% 自动迁移——90% 自动 + 10% 人工,已经比 100% 人工重写快得多。
上手检查清单
决定用 VuReact 迁移前,跑一遍这个清单:
- Vue 版本——VuReact 针对 Vue 3 Composition API 设计,Vue 2 Options API 组件需要先升级或手动改写为
<script setup>。 - 自定义指令数量——超过 5 个自定义指令的项目,人工替换成本会明显上升,评估是否值得。
- 状态管理依赖——Pinia/Vuex 的 store 调用散布在各组件里,编译后变成对全局变量的直接引用,需要后续重构为 React 状态方案。
- 测试覆盖——编译产物必须跑原有测试(改写为 React 测试框架),没有测试的组件先补测试再迁移,否则无法验证编译正确性。
- CI 流程——在 CI 中加入
vureact compile --report步骤,每次提交自动检测新增的manual-review文件。
VuReact 1.8.4 的稳定性修复让编译器从实验工具变成了可依赖的工程工具。它不会消除迁移的所有工作量,但能把最机械的组件转写环节压缩到分钟级,把人力集中到真正需要设计决策的地方。