阅读视图

发现新文章,点击刷新页面。

你的 Vue KeepAlive 组件,VuReact 会编译成什么样的 React 代码?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中内置的 <KeepAlive> 组件经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中 <KeepAlive> 组件的用法。

编译对照

KeepAlive:组件缓存

<KeepAlive> 是 Vue 中用于缓存组件实例的内置组件,可以在动态切换组件时保留组件状态,避免重新渲染和数据丢失。

基础 KeepAlive 使用

  • Vue 代码:
<template>
  <KeepAlive>
    <component :is="currentView" />
  </KeepAlive>
</template>
  • VuReact 编译后 React 代码:
import { KeepAlive } from '@vureact/runtime-core';

<KeepAlive>
  <Component is={currentView} />
</KeepAlive>

从示例可以看到:Vue 的 <KeepAlive> 组件被编译为 VuReact Runtime 提供的 KeepAlive 适配组件,可理解为「React 版的 Vue KeepAlive」。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue <KeepAlive> 的行为,实现组件实例缓存
  2. 状态保持:缓存被移除的组件实例,避免状态丢失
  3. 性能优化:减少不必要的组件重新渲染
  4. React 适配:在 React 环境中实现 Vue 的缓存语义

带 key 的 KeepAlive

为了确保缓存正确工作,建议为动态组件提供稳定的 key

  • Vue 代码:
<template>
  <KeepAlive>
    <component :is="currentComponent" :key="componentKey" />
  </KeepAlive>
</template>
  • VuReact 编译后 React 代码:
<KeepAlive>
  <Component is={currentComponent} key={componentKey} />
</KeepAlive>

key 的重要性

  1. 缓存标识key 用于标识和匹配缓存实例
  2. 稳定切换:确保组件切换时能正确命中缓存
  3. 性能优化:避免不必要的缓存创建和销毁
  4. 最佳实践:始终为动态组件提供稳定的 key

包含与排除控制

<KeepAlive> 支持通过 includeexclude 属性精确控制哪些组件需要缓存。

include:包含特定组件

  • Vue 代码:
<template>
  <KeepAlive :include="['ComponentA', 'ComponentB']">
    <component :is="currentView" />
  </KeepAlive>
</template>
  • VuReact 编译后 React 代码:
<KeepAlive include={['ComponentA', 'ComponentB']}>
  <Component is={currentView} />
</KeepAlive>

exclude:排除特定组件

  • Vue 代码:
<template>
  <KeepAlive :exclude="['GuestPanel', /^Temp/]">
    <component :is="currentView" />
  </KeepAlive>
</template>
  • VuReact 编译后 React 代码:
<KeepAlive exclude={['GuestPanel', /^Temp/]}>
  <Component is={currentView} />
</KeepAlive>

匹配规则

  1. 字符串匹配:精确匹配组件名
  2. 正则表达式:匹配符合模式的组件名
  3. 数组组合:支持字符串和正则的数组组合
  4. key 匹配:同时尝试匹配组件名和缓存 key

最大缓存实例数

通过 max 属性可以限制最大缓存数量,避免内存过度使用。

  • Vue 代码:
<template>
  <KeepAlive :max="3">
    <component :is="currentTab" />
  </KeepAlive>
</template>
  • VuReact 编译后 React 代码:
<KeepAlive max={3}>
  <Component is={currentTab} />
</KeepAlive>

缓存淘汰策略

  1. LRU 算法:淘汰最久未访问的缓存实例
  2. 内存管理:自动清理超出限制的缓存
  3. 性能平衡:在内存使用和性能之间取得平衡
  4. 智能管理:根据访问频率智能管理缓存

缓存生命周期

<KeepAlive> 缓存的组件有特殊的生命周期,可以通过相应的 Hook 监听。

激活与停用生命周期

  • Vue 代码:
<script setup>
import { onActivated, onDeactivated } from 'vue';

onActivated(() => {
  console.log('组件被激活');
});

onDeactivated(() => {
  console.log('组件被停用');
});
</script>
  • VuReact 编译后 React 代码:
import { useActived, useDeactivated } from '@vureact/runtime-core';

function MyComponent() {
  useActived(() => {
    console.log('组件被激活');
  });

  useDeactivated(() => {
    console.log('组件被停用');
  });

  return <div>组件内容</div>;
}

生命周期事件

  1. useActived:组件从缓存中恢复显示时触发
  2. useDeactivated:组件被缓存时触发
  3. 首次渲染:组件首次渲染时也会触发 activated
  4. 最终卸载:组件最终被销毁时触发 deactivated

编译策略总结

VuReact 的 KeepAlive 编译策略展示了完整的组件缓存转换能力

  1. 组件直接映射:将 Vue <KeepAlive> 直接映射为 VuReact 的 <KeepAlive>
  2. 属性完全支持:支持 includeexcludemax 等所有属性
  3. 生命周期适配:将 Vue 生命周期 Hook 转换为 React Hook
  4. 缓存语义保持:完全保持 Vue 的缓存行为和语义

KeepAlive 的工作原理

  1. 实例缓存:组件切出时保留实例在内存中
  2. 状态保持:保持组件的所有状态和数据
  3. DOM 保留:保留组件的 DOM 结构
  4. 智能恢复:切回时快速恢复之前的实例

性能优化策略

  1. 按需缓存:只缓存真正需要的组件
  2. 内存管理:智能管理缓存内存使用
  3. 快速恢复:优化缓存恢复性能
  4. 垃圾回收:及时清理不再需要的缓存

注意事项

  1. 单一子节点<KeepAlive> 只能有一个直接子节点
  2. 组件类型:只能缓存组件元素,不能缓存普通元素
  3. key 要求:缺少稳定 key 时会降级为非缓存渲染

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动实现组件缓存逻辑。编译后的代码既保持了 Vue 的缓存语义和性能优势,又符合 React 的组件设计模式,让迁移后的应用保持完整的组件缓存能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue slot 插槽,VuReact 会编译成什么样的 React 代码?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 <slot> 插槽经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的插槽用法。

编译对照

默认插槽:<slot>

默认插槽是 Vue 中最基本的插槽形式,用于接收父组件传递的默认内容。

  • Vue 代码:
<!-- 子组件 Child.vue -->
<template>
  <div class="container">
    <slot></slot>
  </div>
</template>

<!-- 父组件使用 -->
<Child>
  <p>这是插槽内容</p>
</Child>
  • VuReact 编译后 React 代码:
// 子组件 Child.jsx
function Child(props) {
  return (
    <div className="container">
      {props.children}
    </div>
  );
}

// 父组件使用
<Child>
  <p>这是插槽内容</p>
</Child>

从示例可以看到:Vue 的 <slot> 元素被编译为 React 的 children prop。VuReact 采用 children 编译策略,将插槽出口转换为 React 的标准 children 接收方式,完全保持 Vue 的默认插槽语义——接收父组件传递的子内容并渲染。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue 默认插槽的行为,实现内容分发
  2. React 原生支持:使用 React 标准的 children 机制,无需额外适配
  3. 语法简洁:Vue 的 <slot> 简化为 {children} 表达式
  4. 性能优化:直接使用 React 的原生机制,无运行时开销

具名插槽:<slot name="xxx">

具名插槽允许组件定义多个插槽出口,父组件可以通过名称指定内容插入位置。

  • Vue 代码:
<!-- 子组件 Layout.vue -->
<template>
  <div class="layout">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

<!-- 父组件使用 -->
<Layout>
  <template #header>
    <h1>页面标题</h1>
  </template>
  
  <p>主要内容</p>
  
  <template #footer>
    <p>版权信息</p>
  </template>
</Layout>
  • VuReact 编译后 React 代码:
// 子组件 Layout.jsx
function Layout(props) {
  return (
    <div className="layout">
      <header>{props.header}</header>
      <main>{props.children}</main>
      <footer>{props.footer}</footer>
    </div>
  );
}

// 父组件使用
<Layout
  header={<h1>页面标题</h1>}
  footer={<p>版权信息</p>}
>
  <p>主要内容</p>
</Layout>

从示例可以看到:Vue 的具名插槽 <slot name="xxx"> 被编译为 React 的 props。VuReact 采用 props 编译策略,将具名插槽出口转换为组件的命名 props,完全保持 Vue 的具名插槽语义——通过不同的 prop 名称区分不同的插槽内容。

编译规则

  1. 插槽名映射<slot name="header">header prop
  2. 默认插槽<slot>children prop
  3. props 接收:在组件函数参数中解构接收所有插槽 props

作用域插槽:<slot :prop="value">

作用域插槽允许子组件向插槽内容传递数据,实现更灵活的渲染控制。

  • Vue 代码:
<!-- 子组件 List.vue -->
<template>
  <ul>
    <li v-for="(item, i) in props.items" :key="item.id">
      <slot :item="item" :index="i"></slot>
    </li>
  </ul>
</template>

<!-- 父组件使用 -->
<List :items="users">
  <template v-slot="slotProps">
    <div class="user-item">
      {{ slotProps.index + 1 }}. {{ slotProps.item.name }}
    </div>
  </template>
</List>
  • VuReact 编译后 React 代码:
// 子组件 List.jsx
function List(props) {
  return (
    <ul>
      {props.items.map((item, index) => (
        <li key={item.id}>
          {props.children?.({ item, index })}
        </li>
      ))}
    </ul>
  );
}

// 父组件使用
<List 
  items={users}
  children={(slotProps) => (
    <div className="user-item">
      {slotProps.index + 1}. {slotProps.item.name}
    </div>
  )}
/>

从示例可以看到:Vue 的作用域插槽被编译为 React 的函数 children。VuReact 采用 函数 children 编译策略,将作用域插槽出口转换为接收参数的函数,完全保持 Vue 的作用域插槽语义——子组件通过函数调用向父组件传递数据,父组件通过函数参数接收数据并渲染。

编译规则

  1. 插槽属性转换<slot :item="item" :index="i"> → 函数参数 { item, index }
  2. 函数调用:在渲染位置调用 children() 函数并传递数据
  3. 可选链保护:使用 ?. 避免未提供插槽内容时的错误

具名作用域插槽:<slot name="xxx" :prop="value">

具名作用域插槽结合了具名插槽和作用域插槽的特性。

  • Vue 代码:
<!-- 子组件 Table.vue -->
<template>
  <table>
    <thead>
      <tr>
        <slot name="header" :columns="props.columns"></slot>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in props.data" :key="row.id">
        <slot name="body" :row="row" :columns="props.columns"></slot>
      </tr>
    </tbody>
  </table>
</template>

<!-- 父组件使用 -->
<Table :columns="tableColumns" :data="tableData">
  <template #header="headerProps">
    <th v-for="col in headerProps.columns" :key="col.id">
      {{ col.title }}
    </th>
  </template>
  
  <template #body="bodyProps">
    <td v-for="col in bodyProps.columns" :key="col.id">
      {{ bodyProps.row[col.field] }}
    </td>
  </template>
</Table>
  • VuReact 编译后 React 代码:
// 子组件 Table.jsx
function Table(props) {
  return (
    <table>
      <thead>
        <tr>
          {props.header?.({ columns: props.columns })}
        </tr>
      </thead>
      <tbody>
        {props.data.map((row) => (
          <tr key={row.id}>
            {props.body?.({ row: props.row, columns: props.columns })}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// 父组件使用
<Table
  columns={tableColumns}
  data={tableData}
  header={(headerProps) => (
    <>
      {headerProps.columns.map((col) => (
        <th key={col.id}>{col.title}</th>
      ))}
    </>
  )}
  body={(bodyProps) => (
    <>
      {bodyProps.columns.map((col) => (
        <td key={col.id}>{bodyProps.row[col.field]}</td>
      ))}
    </>
  )}
/>

编译策略

  1. 具名函数 props:具名作用域插槽转换为函数 props
  2. 参数传递:正确传递作用域参数
  3. Fragment 包装:多个元素使用 Fragment 包装
  4. 类型安全:保持 TypeScript 类型定义的完整性

插槽默认内容

Vue 支持在插槽定义处提供默认内容,当父组件没有提供插槽内容时显示。

  • Vue 代码:
<!-- 子组件 Button.vue -->
<template>
  <button class="btn">
    <slot>
      <span class="default-text">点击我</span>
    </slot>
  </button>
</template>
  • VuReact 编译后 React 代码:
// 子组件 Button.jsx
function Button(props) {
  return (
    <button className="btn">
      {props.children || <span className="default-text">点击我</span>}
    </button>
  );
}

默认内容处理规则

  1. 条件渲染:使用 || 运算符检查 children 是否存在
  2. 默认值提供:当 children 为 falsy 值时渲染默认内容
  3. React 模式:使用标准的 React 条件渲染模式

动态插槽名

Vue 支持动态的插槽名称,用于更灵活的插槽选择。

  • Vue 代码:
<!-- 子组件 DynamicSlot.vue -->
<template>
  <div>
    <slot :name="dynamicSlotName"></slot>
  </div>
</template>
  • VuReact 编译后 React 代码:
// 子组件 DynamicSlot.jsx
function DynamicSlot(props) {
  return (
    <div>
      {props[dynamicSlotName]}
    </div>
  );
}

动态插槽处理

  1. 计算属性名:使用对象计算属性语法接收动态插槽
  2. 运行时确定:插槽名在运行时确定

编译策略总结

VuReact 的 <slot> 编译策略展示了完整的插槽系统转换能力

  1. 默认插槽:转换为 React 的 children
  2. 具名插槽:转换为组件的命名 props
  3. 作用域插槽:转换为函数 children 或函数 props
  4. 默认内容:支持插槽默认内容
  5. 动态插槽:支持动态插槽名称

插槽类型映射表

Vue 插槽类型 React 对应形式 说明
<slot> children 默认插槽,作为组件的子元素
<slot name="xxx"> xxx prop 具名插槽,作为组件的属性
<slot :prop="value"> 函数 children 作用域插槽,作为接收参数的函数
<slot name="xxx" :prop="value"> 函数 xxx prop 具名作用域插槽,作为函数属性

性能优化策略

  1. 静态插槽优化:对于静态插槽内容,编译为静态 JSX
  2. 函数缓存:对于作用域插槽,智能缓存渲染函数
  3. 按需生成:根据实际使用情况生成最简化的代码
  4. 类型推导:支持在 TypeScript 中智能推导插槽的类型定义

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写插槽逻辑。编译后的代码既保持了 Vue 的语义和灵活性,又符合 React 的组件设计模式,让迁移后的应用保持完整的内容分发能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue v-slot → 用 VuReact 转换后变成这样的 React 代码

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-slot 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-slot 指令用法。

编译对照

v-slot / #:基础插槽使用

v-slot(简写为 #) 是 Vue 中用于定义和使用插槽的指令,用于实现组件的内容分发和复用。

默认插槽

  • Vue 代码:
<!-- 父组件 -->
<MyComponent>
  <template #default>
    <p>默认插槽内容</p>
  </template>
</MyComponent>

<!-- 或简写 -->
<MyComponent>
  <p>默认插槽内容</p>
</MyComponent>
  • VuReact 编译后 React 代码:
// 父组件
<MyComponent>
  <p>默认插槽内容</p>
</MyComponent>

从示例可以看到:Vue 的默认插槽被直接编译为 React 的 children。VuReact 采用 children 编译策略,将模板插槽转换为 React 的标准 children 传递方式,完全保持 Vue 的默认插槽语义——将内容作为子元素传递给组件。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue 默认插槽的行为,实现内容分发
  2. React 原生支持:使用 React 标准的 children 机制,无需额外适配
  3. 语法简化:Vue 的 <template #default> 简化为直接传递子元素
  4. 性能优化:直接使用 React 的原生机制,无运行时开销

具名插槽

Vue 支持多个具名插槽,用于更灵活的内容分发。

基础具名插槽

  • Vue 代码:
<!-- 父组件 -->
<Layout>
  <template #header>
    <h1>页面标题</h1>
  </template>
  
  <template #main>
    <p>主要内容区域</p>
  </template>
  
  <template #footer>
    <p>页脚信息</p>
  </template>
</Layout>
  • VuReact 编译后 React 代码:
// 父组件
<Layout 
  header={<h1>页面标题</h1>}
  main={<p>主要内容区域</p>}
  footer={<p>页脚信息</p>}
/>

从示例可以看到:Vue 的具名插槽被编译为 React 的 props。VuReact 采用 props 编译策略,将具名插槽转换为组件的 props 属性,完全保持 Vue 的具名插槽语义——通过不同的 prop 名称区分不同的插槽内容。


作用域插槽

Vue 的作用域插槽允许子组件向父组件传递数据,实现更灵活的渲染控制。

基础作用域插槽

  • Vue 代码:
<!-- 父组件 -->
<DataList :items="users">
  <template #item="slotProps">
    <div class="user-item">
      <span>{{ slotProps.user.name }}</span>
      <span>{{ slotProps.user.age }}岁</span>
    </div>
  </template>
</DataList>

<!-- 子组件 DataList.vue -->
<template>
  <ul>
    <li v-for="item in props.items" :key="item.id">
      <slot name="item" :user="item"></slot>
    </li>
  </ul>
</template>
  • VuReact 编译后 React 代码:
// 父组件
<DataList 
  items={users}
  item={(slotProps) => (
    <div className="user-item">
      <span>{slotProps.user.name}</span>
      <span>{slotProps.user.age}岁</span>
    </div>
  )}
/>

// 子组件 DataList.jsx
function DataList(props) {
  return (
    <ul>
      {props.items.map((itemData) => (
        <li key={itemData.id}>
          {props.item?.({ user: itemData })}
        </li>
      ))}
    </ul>
  );
}

从示例可以看到:Vue 的作用域插槽被编译为 React 的函数 props。VuReact 采用 函数 props 编译策略,将作用域插槽转换为接收参数的函数 prop,完全保持 Vue 的作用域插槽语义——子组件通过函数调用向父组件传递数据,父组件通过函数参数接收数据并渲染。


动态插槽名

Vue 支持动态的插槽名称,用于更灵活的插槽选择。

  • Vue 代码:
<BaseLayout>
  <template #[dynamicSlotName]>
    动态插槽内容
  </template>
</BaseLayout>
  • VuReact 编译后 React 代码:
<BaseLayout 
  {...{ [dynamicSlotName]: "动态插槽内容" }}
/>

编译策略

  1. 计算属性名:使用对象计算属性语法 { [key]: value }
  2. 对象展开:通过对象展开语法应用到组件上
  3. 运行时处理:动态插槽名需要在运行时确定

插槽默认内容

Vue 支持在插槽定义处提供默认内容,当父组件没有提供插槽内容时显示。

  • Vue 代码:
<!-- 子组件 Button.vue -->
<template>
  <button class="btn">
    <slot>
      <span>默认按钮文本</span>
    </slot>
  </button>
</template>
  • VuReact 编译后 React 代码:
// 子组件 Button.jsx
function Button(props) {
  return (
    <button className="btn">
      {props.children || <span>默认按钮文本</span>}
    </button>
  );
}

默认内容处理规则

  1. children 检查:检查 children 是否存在
  2. 默认值渲染:当 children 为 falsy 值时渲染默认内容
  3. React 兼容:使用标准的 React 条件渲染模式

编译策略总结

VuReact 的 v-slot 编译策略展示了完整的插槽系统转换能力

  1. 默认插槽:转换为 React 的 children
  2. 具名插槽:转换为组件的 props
  3. 作用域插槽:转换为函数 props
  4. 动态插槽:支持动态插槽名称
  5. 默认内容:支持插槽默认内容

插槽类型映射表

Vue 插槽类型 React 对应形式 说明
默认插槽 children 作为组件的子元素
具名插槽 prop 作为组件的属性
作用域插槽 函数prop 作为接收参数的函数属性
动态插槽 计算属性 使用对象计算属性语法

性能优化策略

  1. 静态插槽优化:对于静态插槽内容,编译为静态 JSX
  2. 函数缓存:对于作用域插槽,智能缓存渲染函数
  3. 按需生成:根据实际使用情况生成最简化的代码
  4. 类型推导:智能推导插槽的类型定义

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写插槽逻辑。编译后的代码既保持了 Vue 的语义和灵活性,又符合 React 的组件设计模式,让迁移后的应用保持完整的内容分发能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue v-model,VuReact 会编译成什么样的 React 代码?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-model 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-model 指令用法。

编译对照

v-model:基础表单双向绑定

v-model 是 Vue 中用于实现表单输入元素双向数据绑定的语法糖,它结合了 v-bindv-on 的功能。

文本输入框

  • Vue 代码:
<input v-model="keyword" />
  • VuReact 编译后 React 代码:
<input
  value={keyword.value}
  onChange={(value) => {
    keyword.value = value;
  }}
/>

从示例可以看到:Vue 的 v-model 指令被编译为 React 的受控组件模式。VuReact 采用 受控组件编译策略,将模板指令转换为 valueonChange 的组合,完全保持 Vue 的双向绑定语义——实现数据与视图的同步更新。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue v-model 的行为,实现双向数据绑定
  2. 受控组件模式:使用 React 标准的受控组件实现
  3. 事件处理:自动处理输入事件和值更新
  4. 响应式集成:与 Vue 的响应式系统无缝集成

不同输入类型的 v-model

Vue 的 v-model 会根据输入元素的类型自动适配,VuReact 也保持了这种智能适配能力。

复选框

  • Vue 代码:
<input type="checkbox" v-model="checked" />
<input type="checkbox" value="vue" v-model="frameworks" />
  • VuReact 编译后 React 代码:
<input
  type="checkbox"
  checked={checked.value}
  onChecked={(e) => {
    checked.value = e.target.checked;
  }}
/>
<input
  type="checkbox"
  value="vue"
  checked={frameworks.value}
  onChange={(e) => {
    frameworks.value = e.target.checked;
  }}
/>

单选按钮

  • Vue 代码:
<input type="radio" value="male" v-model="gender" />
<input type="radio" value="female" v-model="gender" />
  • VuReact 编译后 React 代码:
<input
  type="radio"
  value="male"
  checked={gender.value === 'male'}
  onChange={() => { gender.value = 'male' }}
/>

<input
  type="radio"
  value="female"
  checked={gender.value === 'female'}
  onChange={() => { gender.value = 'female' }}
/>

下拉选择框

  • Vue 代码:
<select v-model="selected">
  <option value="a">选项A</option>
  <option value="b">选项B</option>
</select>
  • VuReact 编译后 React 代码:
<select
  value={selected.value}
  onChange={(e) => {
    selected.value = e.target.value;
  }}
>
  <option value="a">选项A</option>
  <option value="b">选项B</option>
</select>

v-model 修饰符

Vue 的 v-model 支持多种修饰符,用于控制数据更新的时机和格式。

.lazy 修饰符

  • Vue 代码:
<input v-model.lazy="message" />
  • VuReact 编译后 React 代码:
<input
  value={message.value}
  onBlur={(e) => {
    message.value = e.target.value;
  }}
/>

.number 修饰符

  • Vue 代码:
<input v-model.number="age" />
  • VuReact 编译后 React 代码:
<input
  value={age.value}
  onChange={(e) => {
    age.value = Number(e.target.value);
  }}
/>

.trim 修饰符

  • Vue 代码:
<input v-model.trim="username" />
  • VuReact 编译后 React 代码:
<input
  value={username.value}
  onChange={(e) => {
    username.value = e.target.value?.trim();
  }}
/>

修饰符组合

  • Vue 代码:
<input v-model.lazy.trim="search" />
  • VuReact 编译后 React 代码:
<input
  value={search.value}
  onBlur={(e) => {
    search.value = e.target.value?.trim();
  }}
/>

组件 v-model

Vue 3 对组件的 v-model 进行了重大改进,支持多个 v-model 绑定和自定义修饰符。

基础组件 v-model

  • Vue 代码:
<!-- 父组件 -->
<CustomInput v-model="inputValue" />

<!-- 子组件 CustomInput.vue -->
<script setup lang="ts">
  const props = defineProps(['modelValue']);
  const emits = defineEmits(['update:modelValue']);
</script>

<template>
  <input :value="props.modelValue" @input="(e) => emits('update:modelValue', e.target.value)" />
</template>
  • VuReact 编译后 React 代码:
// 父组件
<CustomInput
  modelValue={inputValue.value}
  onUpdateModelValue={(value) => {
    inputValue.value = value;
  }}
/>;

// 子组件 CustomInput.tsx
type ICustomInputProps = {
  modelValue?: any;
  onUpdateModelValue?: (...args: any[]) => any;
}

function CustomInput(props: ICustomInputProps) {
  return (
    <input value={props.modelValue} onChange={(e) => props.onUpdateModelValue?.(e.target.value)} />
  );
}

带参数的 v-model

  • Vue 代码:
<UserForm v-model:name="userName" v-model:email="userEmail" />
  • VuReact 编译后 React 代码:
<UserForm
  name={userName.value}
  onUpdateName={(value) => {
    userName.value = value;
  }}
  email={userEmail.value}
  onUpdateEmail={(value) => {
    userEmail.value = value;
  }}
/>

编译策略总结

VuReact 的 v-model 编译策略展示了完整的双向绑定转换能力

  1. 基础表单元素:将各种输入类型的 v-model 转换为对应的受控组件
  2. 修饰符支持:完整支持 .lazy.number.trim 等修饰符
  3. 组件 v-model:支持组件级别的双向绑定,包括多个 v-model 和自定义修饰符
  4. 事件映射:智能映射 Vue 事件到 React 事件(inputonChange 等)
  5. 类型安全:保持 TypeScript 类型定义的完整性

不同类型元素的编译映射

元素类型 Vue 事件 React 事件 值属性
input[type="text"] input onChange value
textarea input onChange value
input[type="checkbox"] change onChange checked
input[type="radio"] change onChange checked
select change onChange value

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写表单绑定逻辑。编译后的代码既保持了 Vue 的语义和便利性,又符合 React 的表单处理最佳实践,让迁移后的应用保持完整的表单交互能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue v-bind 转 React:VuReact 怎么处理?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-bind/: 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-bind 指令用法。

编译对照

v-bind / ::基础属性绑定

v-bind(简写为 :)是 Vue 中用于动态绑定 HTML 属性、组件 propsclassstyle 的指令。

  • Vue 代码:
<img :src="imageUrl" :class="imageCls" />
  • VuReact 编译后 React 代码:
<img src={imageUrl} className={imageCls} />

从示例可以看到:Vue 的 :src:class 指令被编译为 React 的标准属性语法。VuReact 采用 属性直接编译策略,将模板指令转换为 React 的 JSX 属性,完全保持 Vue 的属性绑定语义——动态地将变量值绑定到元素属性。


class 和 style 的动态绑定

Vue 支持复杂的 classstyle 绑定表达式,VuReact 通过运行时辅助函数处理这些复杂场景。

动态 class 绑定

  • Vue 代码:
<div :class="['card', active && 'is-active', error ? 'has-error' : '']" />
  • VuReact 编译后 React 代码:
import { dir } from '@vureact/runtime-core';

<div className={dir.cls(['card', active && 'is-active', error ? 'has-error' : ''])} />

动态 style 绑定

  • Vue 代码:
<div :style="{ color: textColor, fontSize: size + 'px', 'background-color': bgColor }" />
  • VuReact 编译后 React 代码:
import { dir } from '@vureact/runtime-core';

<div style={dir.style({ color: textColor, fontSize: size + 'px', backgroundColor: bgColor })} />

从示例可以看到:复杂的 class 和 style 绑定被编译为使用 dir.cls()dir.style() 辅助函数。VuReact 采用 复杂绑定运行时处理策略,将 Vue 的复杂表达式转换为运行时函数调用,完全保持 Vue 的动态样式语义

运行时辅助函数的工作原理

  1. dir.cls()

    • 处理数组、对象、字符串等多种 class 格式
    • 自动过滤 falsy 值(false、null、undefined、'')
    • 合并重复的 class 名称
    • 生成最终的 className 字符串
  2. dir.style()

    • 处理对象格式的样式
    • 自动转换 kebab-case 为 camelCase(background-colorbackgroundColor
    • 处理带单位的数值(自动添加 px 等)
    • 生成 React 兼容的 style 对象

编译策略详解

// Vue: :class="{ active: isActive, 'text-danger': hasError }"
// React: className={dir.cls({ active: isActive, 'text-danger': hasError })}

// Vue: :class="[isActive ? 'active' : '', errorClass]"
// React: className={dir.cls([isActive ? 'active' : '', errorClass])}

// Vue: :style="style"
// React: style={dir.style(style)}

无参数 v-bind:对象展开

Vue 支持无参数的 v-bind,用于将整个对象展开为元素的属性。

  • Vue 代码:
<Comp v-bind="props">点击</Comp>
  • VuReact 编译后 React 代码:
import { dir } from '@vureact/runtime-core';

<Comp {...dir.keyless(props)}>点击</Comp>

从示例可以看到:无参数的 v-bind 被编译为使用 dir.keyless() 辅助函数和对象展开语法。VuReact 采用 对象展开编译策略,将 Vue 的对象绑定转换为 React 的对象展开,完全保持 Vue 的对象属性绑定语义

dir.keyless() 辅助函数的作用

  1. 属性冲突处理:处理对象属性与已有属性的冲突
  2. 特殊属性转换:自动转换 classclassNameforhtmlFor
  3. 样式对象处理:识别并正确处理 style 对象
  4. 事件处理:识别并转换事件属性(@clickonClick

布尔属性绑定

Vue 对布尔属性有特殊处理,VuReact 也保持了这种语义。

  • Vue 代码:
<button :disabled="isLoading">提交</button>
<input :checked="isChecked" />
<option :selected="isSelected">选项</option>
  • VuReact 编译后 React 代码:
<button disabled={isLoading}>提交</button>
<input checked={isChecked} />
<option selected={isSelected}>选项</option>

动态属性名绑定

Vue 支持使用动态表达式作为属性名,但不建议这么做,不过 VuReact 也能正确处理。

  • Vue 代码:
<div :[dynamicAttr]="value">内容</div>
  • VuReact 编译后 React 代码:
<div {...{ [dynamicAttr]: value }}>内容</div>

编译策略

  1. 计算属性名:使用对象计算属性语法 { [key]: value }
  2. 对象展开:通过对象展开语法应用到元素上

编译策略总结

VuReact 的 v-bind 编译策略展示了完整的属性绑定转换能力

  1. 基础属性映射:将 Vue 属性绑定精确映射到 React JSX 属性
  2. 复杂样式处理:通过运行时辅助函数支持复杂的 class 和 style 绑定
  3. 对象展开支持:完整支持无参数 v-bind 的对象展开语义
  4. 布尔属性处理:正确处理布尔属性的特殊行为
  5. 动态属性名:支持动态表达式作为属性名
  6. 组件 props 转换:正确处理组件间的 props 传递

性能优化策略

  1. 按需导入:只有使用复杂绑定时才导入 dir 辅助函数
  2. 缓存优化:智能缓存相同表达式的处理结果
  3. 编译期优化:对于简单表达式,直接生成内联逻辑

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写属性绑定逻辑。编译后的代码既保持了 Vue 的语义和功能,又符合 React 的属性处理最佳实践,让迁移后的应用保持完整的 UI 表现能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue v-on 在 React 中 VuReact 会如何实现?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-on/@ 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-on 指令用法。

编译对照

v-on / @:基础事件绑定

v-on(简写为 @)是 Vue 中用于绑定事件监听器的指令,用于响应用户交互。

  • Vue 代码:
<button @click="increment">+1</button>
  • VuReact 编译后 React 代码:
<button onClick={increment}>+1</button>

从示例可以看到:Vue 的 @click 指令被编译为 React 的 onClick 属性。VuReact 采用 事件属性编译策略,将模板指令转换为 React 的标准事件属性,完全保持 Vue 的事件绑定语义——当按钮被点击时,调用 increment 函数。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue v-on 的行为,实现事件监听功能
  2. 命名转换:Vue 的 @click 转换为 React 的 onClick(camelCase 命名)
  3. 函数传递:直接传递函数引用,保持事件处理逻辑
  4. React 原生支持:使用 React 标准的事件系统,无需额外适配

带事件修饰符:高级事件处理

Vue 的事件系统支持丰富的修饰符,用于控制事件行为。VuReact 通过运行时辅助函数处理这些修饰符。

  • Vue 代码:
<button @click.stop.prevent="submit">Submit</button>
  • VuReact 编译后 React 代码:
import { dir } from '@vureact/runtime-core';

<button onClick={dir.on('click.stop.prevent', submit)}>Submit</button>

从示例可以看到:带修饰符的 Vue 事件被编译为使用 dir.on() 辅助函数。VuReact 采用 修饰符运行时处理策略,将复杂的修饰符组合转换为运行时函数调用,完全保持 Vue 的事件修饰符语义

编译策略详解

// Vue: @click.stop.prevent="handler"
// React: onClick={dir.on('click.stop.prevent', handler)}

// Vue: @keyup.enter="search"
// React: onKeyUp={dir.on('keyup.enter', search)}

// Vue: @click.capture="captureHandler"
// React: onClickCapture={dir.on('click.capture', captureHandler)}

运行时辅助函数 dir.on() 的工作原理

  1. 解析修饰符:解析事件名称和修饰符字符串
  2. 创建包装函数:根据修饰符创建事件处理包装函数
  3. 应用修饰符逻辑:在包装函数中实现修饰符对应的行为
  4. 调用原始处理器:最终调用开发者提供的事件处理函数

内联事件处理与参数传递

Vue 支持在模板中直接编写内联事件处理逻辑,VuReact 也能正确处理。

  • Vue 代码:
<button @click="count++">增加</button>
<button @click="sayHello('world')">打招呼</button>
<button @click="handleEvent($event, 'custom')">带事件对象</button>
  • VuReact 编译后 React 代码:
<button onClick={() => count.value++}>增加</button>
<button onClick={() => sayHello('world')}>打招呼</button>
<button onClick={(event) => handleEvent(event, 'custom')}>带事件对象</button>

编译策略

  1. 表达式转换:将 Vue 模板表达式转换为 JSX 箭头函数
  2. 事件对象处理:Vue 的 $event 转换为 React 的事件参数
  3. 参数传递:保持函数调用的参数顺序和值
  4. 响应式更新:自动处理 .value 访问(对于 ref/computed 等变量)

defineEmits 事件与组件通信

对于组件自定义事件,VuReact 也有相应的编译策略。

  • Vue 代码:
<!-- 父组件 -->
<Child @custom-event="handleCustom" />

<!-- 子组件 Child.vue -->
<template>
  <button @click="emits('custom-event', data)">触发事件</button>
</template>

<script setup>
const emits = defineEmits(['custom-event']);
</script>
  • VuReact 编译后 React 代码:
// 父组件使用
<Child onCustomEvent={handleCustom} />;

// 子组件 Child.jsx
function Child(props) {
  return <button onClick={() => props.onCustomEvent?.(data)}>触发事件</button>;
}

编译规则

  1. 事件名转换kebab-case 转换为 camelCasecustom-eventonCustomEvent
  2. emit 调用转换$emit() 转换为 props 回调调用
  3. 可选链保护:添加 ?. 可选链操作符,避免未定义错误
  4. 类型安全:保持 TypeScript 类型定义的一致性

编译策略总结

VuReact 的事件编译策略展示了完整的事件系统转换能力

  1. 基础事件映射:将 Vue 事件指令精确映射到 React 事件属性
  2. 修饰符支持:通过运行时辅助函数完整支持 Vue 事件修饰符
  3. 内联处理:正确处理模板中的内联事件表达式
  4. 自定义事件:支持组件间的自定义事件通信
  5. 类型安全:保持 TypeScript 类型定义的完整性

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写事件处理逻辑。编译后的代码既保持了 Vue 的语义和功能,又符合 React 的事件处理最佳实践,让迁移后的应用保持完整的交互能力。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue v-html 与 v-text 转 React:VuReact 怎么处理?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-html/v-text 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-html 和 v-text 指令用法。

编译对照

v-html:动态 HTML 内容渲染

v-html 是 Vue 中用于将 HTML 字符串动态渲染为 DOM 元素的指令,它会替换元素内的所有内容,并解析 HTML 标签。

  • Vue 代码:
<div v-html="htmlContent"></div>
  • VuReact 编译后 React 代码:
<div dangerouslySetInnerHTML={{ __html: htmlContent }} />

从示例可以看到:Vue 的 v-html 指令被编译为 React 的 dangerouslySetInnerHTML 属性。VuReact 采用 HTML 注入编译策略,将模板指令转换为 React 的特殊属性,完全保持 Vue 的 HTML 渲染语义——将 htmlContent 字符串解析为 HTML 并插入到 DOM 中。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue v-html 的行为,直接渲染 HTML 字符串
  2. 安全警告:React 的 dangerouslySetInnerHTML 属性名本身就提醒开发者注意 XSS 攻击风险
  3. 内容替换:与 Vue 一样,会替换元素内的所有现有内容

v-text:纯文本内容渲染

v-text 是 Vue 中用于将纯文本内容设置到元素内的指令,它会替换元素内的所有内容,但不会解析 HTML 标签。

  • Vue 代码:
<p v-text="message"></p>
  • VuReact 编译后 React 代码:
<p>{message}</p>

从示例可以看到:Vue 的 v-text 指令被编译为 React 的 JSX 插值表达式。VuReact 采用 文本插值编译策略,将模板指令转换为 JSX 的大括号表达式,完全保持 Vue 的文本渲染语义——将 message 作为纯文本内容插入到元素中。

这种编译方式的关键特点在于:

  1. 语义一致性:完全模拟 Vue v-text 的行为,渲染纯文本内容
  2. 自动转义:React 的 JSX 插值会自动转义 HTML 特殊字符,防止 XSS 攻击
  3. 内容替换:与 Vue 一样,会替换元素内的所有现有内容

VuReact 的编译策略确保了从 Vue 到 React 的平滑迁移,开发者无需手动重写内容渲染逻辑。编译后的代码既保持了 Vue 的语义,又符合 React 的安全最佳实践。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue v-for,VuReact 会编译成什么样的 React 代码?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-for 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的 v-for 指令用法。

编译对照

基础数组遍历

最简单的 v-for 指令,用于遍历数组并渲染列表项。

  • Vue 代码:
<li v-for="(item, i) in list" :key="item.id">{{ i }} - {{ item.name }}</li>
  • VuReact 编译后 React 代码:
{
  list.map((item, i) => (
    <li key={item.id}>
      {i} - {item.name}
    </li>
  ));
}

从示例可以看到:Vue 的 v-for 指令被编译为 React 的 map 函数。VuReact 采用 数组映射编译策略,将模板指令转换为 JSX 数组表达式,完全保持 Vue 的列表渲染语义——遍历数组中的每个元素,生成对应的 JSX 元素,并自动处理 key 属性以保证 React 的渲染性能。


对象遍历

v-for 也可以用于遍历对象的属性和值。

  • Vue 代码:
<li v-for="(val, key, i) in obj" :key="key">{{ i }} - {{ key }}: {{ val }}</li>
  • VuReact 编译后 React 代码:
{
  Object.entries(obj).map(([key, val], i) => (
    <li key={key}>
      {i} - {key}: {val}
    </li>
  ));
}

对于对象遍历,VuReact 采用 Object.entries 转换策略,将 Vue 的对象遍历语法转换为 Object.entries(obj).map() 形式。这种编译方式完全模拟 Vue 的对象遍历语义——按顺序遍历对象的键值对,保持 (值, 键, 索引) 的参数顺序,确保数据渲染的一致性。


嵌套 v-for 循环

复杂的嵌套列表渲染,使用多层 v-for 循环。

  • Vue 代码:
<div v-for="category in categories" :key="category.id">
  <h3>{{ category.name }}</h3>
  <ul>
    <li v-for="product in category.products" :key="product.id">
      {{ product.name }} - ${{ product.price }}
    </li>
  </ul>
</div>
  • VuReact 编译后 React 代码:
{
  categories.map((category) => (
    <div key={category.id}>
      <h3>{category.name}</h3>
      <ul>
        {category.products.map((product) => (
          <li key={product.id}>
            {product.name} - ${product.price}
          </li>
        ))}
      </ul>
    </div>
  ));
}

对于嵌套循环,VuReact 采用 嵌套 map 函数编译策略,将 Vue 的嵌套 v-for 转换为嵌套的 map 函数调用。这种编译方式完全保持 Vue 的嵌套循环语义——外层循环的每个迭代都会创建内层循环的完整列表,保持组件结构的层次关系。


v-if + v-for

实际业务中经常需要结合条件进行列表渲染。

  • Vue 代码:
<template v-if="cond" v-for="user in users" :key="user.id">
  <img :src="user.avatar" :alt="user.name" />
  <div class="user-info">
    <h4>{{ user.name }}</h4>
    <p>{{ user.email }}</p>
    <span class="role-badge">{{ user.role }}</span>
  </div>
  <div class="user-actions">
    <button @click="editUser(user.id)">编辑</button>
    <button @click="deleteUser(user.id)" class="danger">删除</button>
  </div>
</template>
  • VuReact 编译后 React 代码:
{
  cond
    ? users.map((user) => (
        <div key={user.id} className="user-card">
          <img src={user.avatar} alt={user.name} />
          <div className="user-info">
            <h4>{user.name}</h4>
            <p>{user.email}</p>
            <span className="role-badge">{user.role}</span>
          </div>
          <div className="user-actions">
            <button onClick={() => editUser(user.id)}>编辑</button>
            <button onClick={() => deleteUser(user.id)} className="danger">
              删除
            </button>
          </div>
        </div>
      ))
    : null;
}

对于带条件的列表渲染,VuReact 展示了智能的条件编译能力

  1. 优先条件编译:将 v-if 转换为三元表达式,包裹整个 v-for 渲染结果
  2. 自动提取 key:当 <template> 标签上存在 :key 属性时,会自动将其传递给内部的第一个子元素
  3. 事件绑定处理@click 转换为 onClick,并自动包装为箭头函数以传递参数
  4. 属性绑定转换:src:alt 等转换为 React 属性语法
  5. 样式类名处理class 转换为 className,符合 React 规范

VuReact 的编译策略完全保持 Vue 的列表渲染语义,同时生成符合 React 最佳实践的代码。


使用 v-for 范围值

Vue 的 v-for 也支持使用数字范围进行迭代。

  • Vue 代码:
<span v-for="n in 5" :key="n">{{ n }}</span>
  • VuReact 编译后 React 代码:
{
  Array.from({ length: 5 }, (_, n) => (
    <span key={n + 1}>{n + 1}</span>
  ));
}

对于范围值迭代,VuReact 采用 Array.from 转换策略,将 Vue 的数字范围语法转换为数组生成和映射。这种编译方式完全模拟 Vue 的范围迭代语义——从 1 开始到指定数字结束(包含),保持迭代顺序和数值的一致性。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue v-if 转 React:VuReact 怎么处理?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 v-if/v-else/v-else-if 指令经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中的条件指令用法。

编译对照

基础 v-if 条件渲染

最简单的 v-if 指令,用于根据条件显示或隐藏元素。

  • Vue 代码:
<div v-if="cond">内容</div>
  • VuReact 编译后 React 代码:
{
  cond ? <div>内容</div> : null;
}

从示例可以看到:Vue 的 v-if 指令被编译为 React 的三元表达式。VuReact 采用 条件表达式编译策略,将模板指令转换为 JSX 内联表达式,完全保持 Vue 的条件渲染语义——当 cond 为真时渲染 <div>,为假时渲染 null(React 中 null 不会被渲染到 DOM)。


v-if 与 v-else 组合

v-ifv-else 组合使用,实现二选一的条件渲染。

  • Vue 代码:
<div v-if="cond">内容</div>
<div v-else>其他内容</div>
  • VuReact 编译后 React 代码:
{
  cond ? <div>内容</div> : <div>其他内容</div>;
}

VuReact 将 v-if/v-else 组合编译为完整的三元表达式完全模拟 Vue 的条件分支语义——两个分支互斥,确保同一时间只有一个元素被渲染。这种编译方式保持了代码的简洁性和可读性,同时与 React 的表达式渲染模式完美契合。


多条件 v-else-if 链

复杂的多条件判断链,使用 v-ifv-else-ifv-else 组合。

  • Vue 代码:
<div v-if="type === 'A'">内容A</div>
<div v-else-if="type === 'B'">内容B</div>
<div v-else>其他内容</div>
  • VuReact 编译后 React 代码:
{
  type === 'A' ? <div>内容A</div> : type === 'B' ? <div>内容B</div> : <div>其他内容</div>;
}

对于多条件链,VuReact 采用嵌套三元表达式编译策略,将 Vue 的 v-else-if 链转换为嵌套的条件表达式。这种编译方式完全保持 Vue 的条件链语义——按顺序检查条件,第一个满足条件的分支被渲染,后续分支被跳过。


复杂业务场景条件渲染

实际业务中的复杂条件渲染,包含嵌套条件、事件绑定、插值表达式等。

  • Vue 代码:
<div v-if="user.role === 'admin' && (user.permissions.includes('write') || isSuperAdmin)">
  <h1>管理员控制面板</h1>
  <button @click="deleteAll">删除所有数据</button>
</div>
<div v-else-if="user.role === 'editor' && articles.length > 0 && !isSuspended">
  <h2>编辑文章 (共{{ articles.length }}篇)</h2>
  <ul>
    <li v-for="article in articles" :key="article.id">{{ article.title }}</li>
  </ul>
</div>
<div v-else-if="user.role === 'viewer' && hasSubscription">
  <h3>订阅用户视图</h3>
  <p>您的订阅将于{{ subscriptionEndDate }}到期</p>
</div>
<div v-else-if="user.role === 'guest' && showTrial">
  <div class="trial-banner">
    <p>试用用户,剩余{{ trialDays }}天</p>
    <button @click="upgrade">升级账户</button>
  </div>
</div>
<div v-else>
  <div class="error-state">
    <p v-if="isLoading">加载中...</p>
    <p v-else-if="errorMessage">{{ errorMessage }}</p>
    <p v-else>无访问权限或账户状态异常</p>
    <button @click="retry">重试 ({{ retryCount }}/3)</button>
  </div>
</div>
  • VuReact 编译后 React 代码:
{
  user.role === 'admin' && (user.permissions.includes('write') || isSuperAdmin) ? (
    <div>
      <h1>管理员控制面板</h1>
      <button onClick={deleteAll}>删除所有数据</button>
    </div>
  ) : user.role === 'editor' && articles.length > 0 && !isSuspended ? (
    <div>
      <h2>编辑文章 (共{articles.length}篇)</h2>
      <ul>
        {articles.map((article) => (
          <li key={article.id}>{article.title}</li>
        ))}
      </ul>
    </div>
  ) : user.role === 'viewer' && hasSubscription ? (
    <div>
      <h3>订阅用户视图</h3>
      <p>您的订阅将于{subscriptionEndDate}到期</p>
    </div>
  ) : user.role === 'guest' && showTrial ? (
    <div>
      <div className="trial-banner">
        <p>试用用户,剩余{trialDays}天</p>
        <button onClick={upgrade}>升级账户</button>
      </div>
    </div>
  ) : (
    <div>
      <div className="error-state">
        {isLoading ? (
          <p>加载中...</p>
        ) : errorMessage ? (
          <p>{errorMessage}</p>
        ) : (
          <p>无访问权限或账户状态异常</p>
        )}
        <button onClick={retry}>重试 ({retryCount}/3)</button>
      </div>
    </div>
  );
}

对于复杂的业务场景,VuReact 展示了完整的条件编译能力

  1. 复杂条件表达式:将 Vue 的复杂条件逻辑(&&||、函数调用等)原样转换为 JSX 表达式
  2. 事件绑定转换@click 转换为 onClick,保持事件语义
  3. 插值表达式{{ }} 转换为 { },保持数据绑定
  4. 样式类名转换class 转换为 className,符合 React 规范

VuReact 的编译策略完全保持 Vue 的条件渲染语义,同时生成符合 React 最佳实践的代码,提高可维护性。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue 路由,VuReact 会编译成什么样的 React 路由?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天我们从 Vue Router 宏观对照入手,看看 Vue 中的路由组件、API 与入口结构,经过 VuReact 编译后会变成什么样的 React 路由代码。

另外,本文仅展示部分路由组件与 API,实际上完整适配还包括路由类型接口等更多内容,详情请查阅 VuReact Router 文档。

前置约定

为避免示例冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue Router API 用法与核心行为。

编译对照

router 组件:<router-link> / <router-view>

Vue 的路由组件在 React 中被映射为 @vureact/router 提供的适配组件。

  • Vue 代码:
<template>
  <router-link to="/home">Home</router-link>
  <router-view />
</template>
  • VuReact 编译后 React 代码:
import { RouterLink, RouterView } from '@vureact/router';

return (
  <>
    <RouterLink to="/home">Home</RouterLink>
    <RouterView />
  </>
);

RouterLink 在 React 中同样支持字符串 to、对象 toactiveClassNamecustomRender 等 Vue 风格用法;RouterView 负责渲染当前匹配路由组件,并保持嵌套路由、路由守卫与元字段的执行顺序。


路由配置:createRouter + history

Vue Router 的创建方式在 VuReact 中保持语义一致,但依赖会替换为 @vureact/router

  • Vue 代码:
import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';

export default createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
  ],
});
  • VuReact 编译后 React 代码:
import { createRouter, createWebHistory } from '@vureact/router';
import Home from './views/Home';

export default createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
  ],
});

这说明:

  • createRouter / createWebHistory 等 API 名称保持不变;
  • 仅依赖路径会被替换成 @vureact/router
  • Vue Router 的路由记录、嵌套路由、meta 字段可直接保留。

入口注入:RouterProvider

如果启用了自动适配,VuReact 会在编译后自动调整入口文件,将原 <App /> 替换为路由实例的 RouterProvider

  • 生成后的 React 入口文件:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import RouterInstance from './router/index';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterInstance.RouterProvider />
  </StrictMode>,
);

该入口结构体现了 Vue 路由到 React 路由适配的宏观变化:

  • Vue 的路由配置文件继续作为路由实例入口;
  • React 入口通过 RouterProvider 挂载路由上下文;
  • 因此无需手动改写业务路由逻辑,只需保证路由定义规范。

运行时 API:useRouter / useRoute

Vue 的组合式路由 API 在 React 中仍保留相同语义。

  • Vue 代码:
const router = useRouter();
const route = useRoute();

const goHome = () => {
  router.push('/home');
};
  • VuReact 编译后 React 代码:
import { useRouter, useRoute } from '@vureact/router';

const router = useRouter();
const route = useRoute();

const goHome = useCallback(() => {
  router.push('/home');
}, [router]);

useRouter()useRoute() 仍然支持编程式导航、参数读取、meta 等字段,且使用方式与 Vue Router 组合式 API 语义保持一致。


自动适配

当编译器检测到项目中使用 Vue Router 时,会自动:

  • import ... from 'vue-router' 替换为 import ... from '@vureact/router'
  • 将路由配置文件产物变更为 @vureact/router 的路由实例;
  • 将入口文件自动改写为 RouterProvider 渲染。

配置示例:

import { defineConfig } from '@vureact/compiler-core';

export default defineConfig({
  router: {
    // 路由入口文件路径(即调用并默认导出 createRouter() 的地方)
    configFile: 'src/router/index.ts',
  },
});

手动适配

以下方案为通用建议,具体实现细节请开发者根据实际项目需求进行调整。

当选项 output.bootstrapVite 或者 router.autoSetupfalse 时,自动适配不可用,需要手动完成:

  • 导出 Vue Router 的 createRouter() 实例;
  • 在 React 入口文件中,将原本渲染 <App /> 的代码替换为 @vureact/router 路由实例所提供的 <RouterProvider /> 组件。

手动适配的核心是:保留 Vue Router 的路由定义与嵌套路由结构,导出路由器实例,替换 React 入口渲染方式。

相关资源


如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue 3 defineAsyncComponent(),VuReact 会编译成什么样的 React?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中用于异步组件的 defineAsyncComponent() 经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中 defineAsyncComponent 的 API 用法与核心行为。

编译对照

Vue defineAsyncComponent() → React defineAsyncComponent()

defineAsyncComponent 是 Vue 3 中用于定义异步组件的 API,它允许你按需加载组件,优化应用性能。VuReact 会将其编译为同名的 defineAsyncComponent,让 React 中也能获得同样的异步组件能力。

  • Vue 代码:
<script setup>
  import { defineAsyncComponent } from 'vue';

  const AsyncComponent = defineAsyncComponent(() =>
    import('./components/AsyncComponent.vue')
  );
</script>

<template>
  <AsyncComponent />
</template>
  • VuReact 编译后 React 代码:
import { defineAsyncComponent } from '@vureact/runtime-core';

const AsyncComponent = defineAsyncComponent(() =>
  import('./components/AsyncComponent')
);

function MyComponent() {
  return <AsyncComponent />;
}

VuReact 提供的 defineAsyncComponentVue defineAsyncComponent 的适配 API,可理解为「React 版的 Vue defineAsyncComponent」,完全模拟 Vue defineAsyncComponent 的异步加载行为——支持懒加载、加载状态处理、错误处理等完整功能。

defineAsyncComponent 高级用法

defineAsyncComponent 在 Vue 3 中支持多种配置选项,如加载状态组件、错误处理组件、超时设置等。VuReact 会将其编译为相应的 React 配置,保持功能一致性。

  • Vue 代码:
<script setup>
  import { defineAsyncComponent } from 'vue';

  const AsyncComponent = defineAsyncComponent({
    loader: () => import('./components/HeavyComponent.vue'),
    loadingComponent: LoadingSpinner,
    errorComponent: ErrorDisplay,
    delay: 200,
    timeout: 3000,
    suspensible: true,
  });
</script>
  • VuReact 编译后 React 代码:
import { defineAsyncComponent } from '@vureact/runtime-core';
import LoadingSpinner from './components/LoadingSpinner';
import ErrorDisplay from './components/ErrorDisplay';

const AsyncComponent = defineAsyncComponent({
  loader: () => import('./components/HeavyComponent'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorDisplay,
  delay: 200,
  timeout: 3000,
  suspensible: true,
});

VuReact 提供的 defineAsyncComponent 支持 所有 Vue defineAsyncComponent 的配置选项,包括 loaderloadingComponenterrorComponentdelaytimeoutsuspensible 等,完全模拟 Vue defineAsyncComponent 的高级功能——在 React 中实现与 Vue 一致的异步组件体验。

请注意,hydrate 选项不支持,但保留了该选项进行兼容,无实际功能。

相关资源


如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue 3 TS 类型声明,VuReact 会处理成什么样的 React?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:VuReact 如何自动分析 Vue 3 中的响应式依赖,精准生成 React Hooks 的依赖数组

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 和 React 的响应式与依赖追踪机制。

编译对照

Vue 自动依赖分析 → React Hook 依赖数组生成

VuReact 编译器内置了自动依赖分析能力,遵循 React 规则,智能分析顶层箭头函数顶层变量声明中的响应式访问,并生成准确的依赖数组。

  • Vue 代码:
<script setup lang="ts">
  import { reactive, ref } from 'vue';

  const count = ref(0);
  const foo = ref(0);
  const state = reactive({ foo: 'bar', bar: { c: 1 } });

  const fn1 = () => {
    count.value += state.bar.c;
    console.log(count.value);
  };

  const fn = () => {};

  const fn2 = () => {
    const c = foo.value;
    fn();

    const fn4 = () => {
      state.bar.c--;
      c + count.value;
    };
  };

  const fn3 = () => {
    foo.value++;

    const state = ref('fake');
    const count = state.value + 'yoxi';
    count.charAt(1);
  };
</script>
  • VuReact 编译后 React 代码:
const count = useVRef(0);
const foo = useVRef(0);
const state = useReactive({ foo: 'bar', bar: { c: 1 } });

const fn1 = useCallback(() => {
  count.value += state.bar.c;
  console.log(count.value);
}, [count.value, state.bar?.c]);

const fn = () => {};

const fn2 = useCallback(() => {
  const c = foo.value;
  fn();

  const fn4 = () => {
    state.bar.c--;
    c + count.value;
  };
}, [foo.value, state.bar?.c, count.value]);

const fn3 = useCallback(() => {
  foo.value++;

  const state = useVRef('fake');
  const count = state.value + 'yoxi';
  count.charAt(1);
}, [foo.value]);

这段对比展示了:

  • fn1 会被识别为顶层箭头函数并收集 count.valuestate.bar.c
  • fn2 会溯源 c 并忽略局部函数 fn4
  • fn3 会忽略函数内部新建的响应式变量,只收集外部依赖 foo.value

Vue 组合访问与别名追踪

VuReact 也会对复杂别名链和解构访问进行溯源。

  • Vue 代码:
<script setup lang="ts">
  const objRef = ref({ a: 1, b: { c: 1 } });
  const listRef = ref([1, 2, 3]);
  const aliasA = state.foo;
  const aliasB = aliasA;
  const aliasC = aliasB;
  const { foo: stateFoo } = state;
  const [first] = listRef.value;

  const traceFn = () => {
    aliasC;
  };

  const destructureFn = () => {
    stateFoo;
    first;
  };
</script>
  • VuReact 编译后 React 代码:
const objRef = useVRef({ a: 1, b: { c: 1 } });
const listRef = useVRef([1, 2, 3]);
const aliasA = useMemo(() => state.foo, [state.foo]);
const aliasB = useMemo(() => aliasA, [aliasA]);
const aliasC = useMemo(() => aliasB, [aliasB]);
const { foo: stateFoo } = useMemo(() => state, [state]);
const [first] = useMemo(() => listRef.value, [listRef.value]);

const traceFn = useCallback(() => {
  aliasC;
}, [aliasC]);

const destructureFn = useCallback(() => {
  stateFoo;
  first;
}, [stateFoo, first]);

这样可见:

  • alias 链会被逐层溯源到真实响应式来源;
  • 解构后的变量也会通过 useMemo 转换为可追踪依赖。

Vue 顶层变量声明 → React useMemo 依赖数组生成

  • Vue 代码:
<script setup lang="ts">
  const fooRef = ref(0);
  const reactiveState = reactive({ foo: 'bar', bar: { c: 1 } });

  const memoizedObj = {
    title: 'test',
    bar: fooRef.value,
    add: () => {
      reactiveState.bar.c++;
    },
  };

  let staticObj = {
    foo: 1,
    state: { bar: { c: 1 } },
  };

  const reactiveList = [fooRef.value, 1, 2];

  const mixedList = [
    { name: reactiveState.foo, age: fooRef.value },
    { name: 'A', age: 20 },
  ];

  const nestedObj = {
    a: {
      b: {
        c: reactiveList[0],
        d: () => {
          return memoizedObj.bar;
        },
      },
      e: mixedList,
    },
  };
</script>
  • VuReact 编译后 React 代码:
const memoizedObj = useMemo(
  () => ({
    title: 'test',
    bar: fooRef.value,
    add: () => {
      reactiveState.bar.c++;
    },
  }),
  [fooRef.value, reactiveState.bar?.c],
);

let staticObj = {
  foo: 1,
  state: {
    bar: {
      c: 1,
    },
  },
};

const reactiveList = useMemo(() => [fooRef.value, 1, 2], [fooRef.value]);

const mixedList = useMemo(
  () => [
    { name: reactiveState.foo, age: fooRef.value },
    { name: 'A', age: 20 },
  ],
  [reactiveState.foo, fooRef.value],
);

const nestedObj = useMemo(
  () => ({
    a: {
      b: {
        c: reactiveList[0],
        d: () => {
          return memoizedObj.bar;
        },
      },
      e: mixedList,
    },
  }),
  [reactiveList[0], memoizedObj.bar, mixedList],
);

这里的核心对比是:

  • memoizedObj 会收集对象内部的响应式字段与方法依赖;
  • staticObj 因为不含响应式访问,不会被优化为 useMemo
  • reactiveListmixedListnestedObj 会根据结构递归补齐依赖数组。

自动依赖分析的三大原则

  1. 仅分析顶层可优化表达式:局部函数、嵌套作用域不纳入顶层 Hook 自动优化;
  2. 遵循 React 依赖规则:只收集函数/变量外部的响应式访问,而非内部局部变量;
  3. 避免过度优化:无外部响应式依赖的顶层箭头函数和变量不会被强制转换为 Hook。

为什么这很关键?

在 React 中,函数组件每次渲染会重新创建顶层函数与变量。如果这些顶层表达式依赖响应式状态且未获得稳定性处理,会带来:

  • 不必要的子组件重新渲染;
  • 频繁的 Hook 重新计算;
  • 性能不可控的回调变化。

VuReact 在编译阶段自动生成准确依赖数组,既保留了 Vue 写法的简洁性,又实现了 React 端的性能优化。

相关资源


如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

Vue 3 defineOptions 宏,用 VuReact 编译成 React 长什么样?

VuReact 是一个语义感知、约定驱动、支持渐进迁移的编译器,能把 Vue 3 代码一键转成标准可维护的 React 18+ 代码。

今天我们继续拆解核心 API:Vue 3 <script setup> 里的 defineOptions 宏,经过 VuReact 编译后在 React 中如何呈现?

前置约定

为了示例清爽、理解无歧义,先统一两个规则:

  1. 只保留核心逻辑,省略外层包裹与无关配置;
  2. 默认你已熟悉 Vue 3 defineOptions 的用法与语义。

编译对照:Vue defineOptions → React

1. Vue defineOptions({ name }) → React 组件命名

defineOptions 是 Vue 3 用于组件额外配置的宏,最常用就是指定组件 name。 在 React 中没有完全对应的宏,VuReact 会把 name 直接映射为组件函数名,保持语义一致。

Vue 代码

<script setup lang="ts">
  defineOptions({
    name: 'MyComponent'
  })
</script>

VuReact 编译后 React 代码

const MyComponent = () => {
  return <></>
}

export default MyComponent

defineOptions({ name }) 不会生成任何运行时 Hook,仅作为编译期信息,用来给 React 组件“起名字”,让 DevTools、调用栈保持和 Vue 一致。


2. Vue defineOptions 其他配置 → React 忽略/编译提示

defineOptions 还支持 inheritAttrscustomOptions 等配置。 由于 React 组件机制与 Vue 不同,无法直接映射,VuReact 会做保守处理:

  • inheritAttrs:React 无对应概念,直接忽略
  • customOptions:非标准配置,忽略并可在编译期提示
  • 其他扩展选项:统一忽略

Vue 代码

<script setup lang="ts">
  defineOptions({
    name: 'MyComponent',
    inheritAttrs: false
  })
</script>

VuReact 编译后 React 代码

const MyComponent = () => {
  return <></>
}

export default MyComponent
// inheritAttrs 在 React 中无直接对应,已忽略

这样处理的好处:不向 React 注入无用运行时代码,保持产物干净、符合 React 最佳实践。


3. 最佳实践:用 @vr-name 显式指定组件名

如果你希望100% 保留组件名语义,推荐使用 VuReact 官方推荐的注释约定:

<script setup lang="ts">
// @vr-name: MyComponent
</script>

编译后会稳定生成对应名称的 React 组件,比 defineOptions({ name }) 更可靠、更符合编译约定。

核心总结

  • defineOptions({ name }) → 编译为 React 组件名,无运行时开销
  • inheritAttrs 等 → React 无对应,直接安全忽略
  • 推荐用 // @vr-name: 组件名 替代,更稳定、更标准

VuReact 始终遵循:保留语义、不造多余运行时、符合 React 规范

相关资源


✨ 对你有帮助的话,欢迎 点赞 + 收藏 + 关注,持续更新 VuReact 编译原理实战~

你的 Vue 3 defineEmits(),VuReact 会编译成什么样的 React?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 defineEmits 宏经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中 defineEmits 的 API 用法与核心行为。

编译对照

Vue defineEmits → React props 事件回调映射

defineEmits 是 Vue 3 <script setup> 中用于声明组件自定义事件的宏,它会把事件名称和参数类型定义为函数签名。VuReact 会将它编译为 React props 的事件回调形式,并对事件名做驼峰映射。

  • Vue 代码:
<script setup lang="ts">
  defineProps<{ name?: string }>();

  const emit = defineEmits<{
    (e: 'save-item', payload: { id: string }): void;
    (e: 'update:name', value: string): void;
  }>();

  const submit = () => {
    emit('save-item', { id: '1' });
    emit('update:name', 'next');
  };
</script>
  • VuReact 编译后 React 代码:
type ICompProps = {
  name?: string;
  onSaveItem?: (payload: { id: string }) => void;
  onUpdateName?: (value: string) => void;
};

const submit = useCallback(() => {
  props.onSaveItem?.({ id: '1' });
  props.onUpdateName?.('next');
}, [props.onSaveItem, props.onUpdateName]);

从示例可以看到:Vue 的 defineEmits 不会直接编译为运行时 Hook,而是转换为 React 组件 props 中的回调函数。VuReact 会将事件名 save-item / update:name 映射为 onSaveItem / onUpdateName,并保留参数类型定义,实现了事件签名与 React props 回调的无缝对接


Vue v-model:xxx → React 双向绑定 props + 事件映射

此外,子组件中定义的 update:xxx 这类事件,通常用于实现 Vue 中父子组件的双向数据绑定,父组件会以 v-model:xxx="value" 的形式使用。VuReact 充分考虑了这种模式,能够精准地进行转换:

  • 父组件 Vue 代码:
<template>
  <Child v-model:name="current" />
</template>

<script setup>
  // @vr-name: Parent
  const current = ref('');
</script>
  • VuReact 编译后 React 代码:
const Parent = memo(() => {
  const current = useVRef('');
  return <Child name={current.value} onUpdateName={value => current.value = value} />
});

Vue emit 调用 → React props 回调调用

在 Vue 中,emit('event-name', payload) 触发组件自定义事件;在 React 中,VuReact 会把它编译为 props.onEventName?.(payload) 的调用形式。

  • Vue 代码:
<script setup lang="ts">
  const emit = defineEmits<{
    (e: 'submit', value: string): void;
  }>();

  const handleSubmit = () => {
    emit('submit', 'ok');
  };
</script>
  • VuReact 编译后 React 代码:
type ICompProps = {
  onSubmit?: (value: string) => void;
};

const handleSubmit = useCallback(() => {
  props.onSubmit?.('ok');
}, [props.onSubmit]);

VuReact 会对 emit 的事件名和参数进行类型映射,并在必要时自动为 useCallback 生成依赖数组,让 React 端的回调引用保持稳定,同时避免开发者手动维护依赖


Vue defineEmits 兼容事件名映射规则

VuReact 支持将 Vue 的短横线事件名、冒号事件名等映射为 React 的驼峰命名回调:

  • save-itemonSaveItem
  • update:nameonUpdateName
  • closeonClose

这种映射方式与 React 事件 props 习惯一致,也保持了 Vue 事件声明的语义。

🔗 相关资源


✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

你的 Vue 3 生命周期,VuReact 会编译成什么样的 React?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的生命周期钩子经过 VuReact 编译后会变成什么样的 React 代码?

前置约定

为避免示例代码冗余导致理解偏差,先明确两个小约定:

  1. 文中 Vue / React 代码均为核心逻辑简写,省略完整组件包裹、无关配置等内容;
  2. 默认读者已熟悉 Vue 3 中生命周期钩子例如 onMounted、onBeforeMount、onUpdated、onBeforeUpdate、onBeforeUnmount、onUnmounted 的 API 用法与核心行为。

编译对照

Vue onMounted() → React useMounted()

onMounted 是 Vue 3 中用于组件首次挂载后执行逻辑的生命周期钩子,适合放初始化请求、订阅启动、DOM 相关准备等操作。VuReact 会将它编译为 useMounted,让 React 端也能在组件挂载后执行一次性副作用。

  • Vue 代码:
<script setup>
  import { onMounted } from 'vue';

  onMounted(() => {
    console.log('组件已挂载');
  });
</script>
  • VuReact 编译后 React 代码:
import { useMounted } from '@vureact/runtime-core';

useMounted(() => {
  console.log('组件已挂载');
});

从示例可以看到:Vue 的 onMounted() 被翻译为 useMounted。VuReact 提供的 useMountedonMounted 的适配 API完全模拟 Vue onMounted 的首次挂载后执行时机

Vue onBeforeMount() → React useBeforeMount()

onBeforeMount 是 Vue 3 中用于组件挂载前执行逻辑的钩子,适合放需要在布局阶段之前准备的内容。VuReact 会将它编译为 useBeforeMount,基于 React 的布局效果在挂载前执行。

  • Vue 代码:
<script setup>
  import { onBeforeMount } from 'vue';

  onBeforeMount(() => {
    console.log('组件即将挂载');
  });
</script>
  • VuReact 编译后 React 代码:
import { useBeforeMount } from '@vureact/runtime-core';

useBeforeMount(() => {
  console.log('组件即将挂载');
});

VuReact 提供的 useBeforeMountonBeforeMount 的适配 API完全模拟 Vue onBeforeMount 的首次挂载前时机

Vue onBeforeUpdate() → React useBeforeUpdate()

onBeforeUpdate 是 Vue 3 中用于跳过首次挂载,仅在组件更新前执行的钩子,适合放变更前校验、记录旧值、提前准备等逻辑。VuReact 会将它编译为 useBeforeUpdate,并支持依赖数组以控制触发时机。

  • Vue 代码:
<script setup>
  import { reactive, onBeforeUpdate } from 'vue';

  const state = reactive({ count: 0 });

  onBeforeUpdate(() => {
    console.log('更新前,当前 count:', state.count);
  });
</script>
  • VuReact 编译后 React 代码:
import { useReactive, useBeforeUpdate } from '@vureact/runtime-core';

const state = useReactive({ count: 0 });

useBeforeUpdate(
  () => {
    console.log('更新前,当前 count:', state.count);
  },
  [state.count],
);

从示例可以看到:Vue 的 onBeforeUpdate() 被翻译为 useBeforeUpdate。VuReact 提供的 useBeforeUpdateonBeforeUpdate 的适配 API完全模拟 Vue onBeforeUpdate 的更新前触发时机。当 React 对应 API 需要依赖数组时,deps 数组可用于只在指定值变化时触发,VuReact 会在编译阶段自动分析依赖并映射到对应依赖数组,避免开发者手动管理依赖

Vue onUpdated() → React useUpdated()

onUpdated 是 Vue 3 中用于组件更新后执行逻辑的钩子,适合放读取最新渲染结果、执行后续同步等操作。VuReact 会将它编译为 useUpdated,并支持可选依赖数组来精确控制触发条件。

  • Vue 代码:
<script setup>
  import { reactive, onUpdated } from 'vue';

  const state = reactive({ count: 0 });

  onUpdated(() => {
    console.log('组件更新后,count:', state.count);
  });
</script>
  • VuReact 编译后 React 代码:
import { useReactive, useUpdated } from '@vureact/runtime-core';

const state = useReactive({ count: 0 });

useUpdated(
  () => {
    console.log('组件更新后,count:', state.count);
  },
  [state.count],
);

VuReact 提供的 useUpdatedonUpdated 的适配 API完全模拟 Vue onUpdated 的更新后执行时机。如果 React API 使用 deps 数组,VuReact 会自动分析依赖并生成对应的数组,无需开发者手动维护依赖

Vue onBeforeUnmount() → React useBeforeUnMount()

onBeforeUnmount 是 Vue 3 中用于组件卸载前执行的钩子,适合放动画停止、资源解绑、日志上报等清理前逻辑。VuReact 会将它编译为 useBeforeUnMount,在卸载前执行。

  • Vue 代码:
<script setup>
  import { onBeforeUnmount } from 'vue';

  onBeforeUnmount(() => {
    console.log('组件即将卸载');
  });
</script>
  • VuReact 编译后 React 代码:
import { useBeforeUnMount } from '@vureact/runtime-core';

useBeforeUnMount(() => {
  console.log('组件即将卸载');
});

VuReact 提供的 useBeforeUnMountonBeforeUnmount 的适配 API完全模拟 Vue onBeforeUnmount 的卸载前时机

Vue onUnmounted() → React useUnmounted()

onUnmounted 是 Vue 3 中用于组件卸载时执行逻辑的钩子,适合放最终资源释放、异步取消、上报日志等收尾逻辑。VuReact 会将它编译为 useUnmounted,在组件卸载时执行。

  • Vue 代码:
<script setup>
  import { onUnmounted } from 'vue';

  onUnmounted(() => {
    console.log('组件已卸载');
  });
</script>
  • VuReact 编译后 React 代码:
import { useUnmounted } from '@vureact/runtime-core';

useUnmounted(() => {
  console.log('组件已卸载');
});

VuReact 提供的 useUnmountedonUnmounted 的适配 API完全模拟 Vue onUnmounted 的卸载时机

🔗 相关资源

✨ 如果你觉得本文对你理解 VuReact 有帮助,欢迎点赞、收藏、关注!

❌