VuReact 已于近日完成对 Vue 3.4+
defineModel宏的完整编译支持 #56。开发者现在可以在 Vue 源码中直接使用defineModel声明v-model双向绑定,VuReact 会将其编译为标准、可维护的 React 代码。
defineModel 是 Vue 3.4 中正式发布的编译器宏,用于简化 v-model 双向绑定的声明。VuReact 将其编译为 useVRef + useUpdated 的组合:useVRef 将 prop 值转为响应式 ref,useUpdated 在值变化时自动触发 onUpdate:xxx 回调通知父组件。组件内直接修改 .value 即可触发父组件更新,与 Vue 开发体验完全一致。
代码示例
Vue 源码(使用 defineModel):
html
<script setup lang="ts">
// 声明 "state" prop,由父组件通过 v-model:state 使用
const state = defineModel<string>('state');
// 声明带选项的 "modelValue" prop,由父组件通过 v-model 使用
const modelValue = defineModel({ default: 'xxx' });
// 声明带选项的 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel<number>('count', {
type: Number,
default: 0,
required: true,
});
const update = () => {
state.value = 'hello';
count.value++;
};
</script>
<template>
<input v-model="modelValue" />
<div>Parent bound v-model is: {{ count }}</div>
<button @click="update">Increment</button>
</template>
VuReact 编译后的 React 产物:
ts
import { useCallback, memo } from 'react';
import { useVRef, useUpdated } from '@vureact/runtime-core';
export type IChildProps = {
state?: string;
modelValue?: string;
count: number;
} & {
onUpdateState?: (arg: string) => void;
onUpdateModelValue?: (arg: string) => void;
onUpdateCount?: (arg: number) => void;
};
const Child = memo((props: IChildProps) => {
const state = useVRef<string>(props.state);
const modelValue = useVRef<string>(props.modelValue ?? 'xxx');
const count = useVRef<number>(props.count ?? 0);
const update = useCallback(() => {
state.value = 'hello';
count.value++;
}, [state.value, count.value]);
useUpdated(() => {
props.onUpdateState?.(state.value);
}, [state.value]);
useUpdated(() => {
props.onUpdateModelValue?.(modelValue.value);
}, [modelValue.value]);
useUpdated(() => {
props.onUpdateCount?.(count.value);
}, [count.value]);
return (
<>
<input
value={modelValue}
onChange={(e) => {
modelValue.value = e.target.value;
}}
/>
<div>Parent bound v-model is:{count.value}</div>
<button onClick={update}>Increment</button>
</>
);
});
export default Child;
从示例可以看到:Vue 的 defineModel 被分解为三部分:
- prop 类型声明 →
IChildProps类型中的非事件字段(state?、modelValue?、count) - 事件回调声明 →
IChildProps类型中的onUpdateXxx事件回调字段 - 运行时响应式 →
useVRef将 prop 初始值转为响应式 ref,useUpdated监听值变化并自动调用父组件传入的回调
模板中的 v-model 则被编译为 React 受控组件的 value + onChange 模式。
本次支持的 defineModel 能力
| 能力 | 说明 |
|---|---|
| 基础双向绑定 | defineModel<string>() 编译为 prop 类型声明 + onUpdateXxx 事件回调 + useVRef 运行时响应式 |
| 自定义 prop 名称 | defineModel<string>('state') 支持指定 prop 名称,父组件通过 v-model:state 使用 |
| 类型与默认值 | 支持 type、default、required 选项,required: true 在类型定义中转为必填,default 通过 ?? 空值合并实现 |
模板 v-model 绑定 |
编译为 React 受控组件的 value + onChange 模式 |
暂不支持的 defineModel 用法
| 用法 | 说明 |
|---|---|
const [model, modifiers] = defineModel() |
返回值数组解构(Vue 3.4+ 实验特性) |
get / set 自定义存取器 |
自定义 prop 读写逻辑 |
validator 验证函数 |
prop 值验证 |
上述不支持的场景,建议使用标准 defineModel 写法或直接使用 useVRef 自行实现自定义逻辑。
关于 VuReact
VuReact 是一个将 Vue 3 单文件组件编译为纯 React 组件的 AST 编译器,采用语义级编译路线,产物为纯 React 代码,不依赖 Vue 运行时。目前已完成 defineProps、defineEmits、defineExpose、defineModel 等核心宏的语义映射,以及 ref、computed、watch、scoped style 等 Vue 特性的完整适配。
相关链接:
- GitHub:github.com/vureact-js/...(欢迎 Star ⭐~开源不易)
- 官方文档:vureact.top
- defineModel 编译对照:vureact.top/guide/seman...
写在最后
defineModel 是 Vue 3.4 引入的重要特性,VuReact 完成对其的编译支持,意味着 Vue 开发者可以在迁移到 React 时继续使用这套熟悉的双向绑定写法,不必手动改写为 useState + props.onChange 的模式。
如果你正在经历 Vue → React 的迁移,欢迎在评论区分享你的场景和经验,也欢迎给 VuReact 点个 Star 支持开源~
扩展阅读: