React × Vue

受控表单与 v-model

React 显式连接 value 和事件处理函数;Vue 使用 v-model 描述表单值的双向绑定。

核心结论

两边都可以让组件状态成为表单的事实来源。React 显式写出 value → onChange → setter;Vue 的 v-model 是属性绑定与更新事件的语法糖。

表单数据流

组件关系与数据流

Form StateComponentBindingDOM Control

差异焦点读取、更新和回写都是显式代码,state 是唯一数据源。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: React 受控表单数据流
  accDescr: input 事件读取 DOM value,onChange 调用 setter 更新 state,组件重新渲染后再通过 value prop 把状态写回表单控件。
  input[("input / select DOM")] -.->|"onChange event"| handler{{"读取 event.target.value"}}
  handler --> setter{{"调用 setter"}}
  setter --> state(["name / direction state"])
  state --> demo["ReactDemo render"]
  demo -->|"value prop"| input
  class state state
  class demo component
  class handler,setter process
  class input resource
  classDef state fill:#fffdf8,stroke:#149eca,color:#25221d,stroke-width:1.2px
  classDef component fill:#e4f5fa,stroke:#149eca,color:#25221d,stroke-width:2px,font-weight:600
  classDef process fill:#f4fafb,stroke:#149eca,color:#25221d,stroke-width:1.6px
  classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4px

事件把 DOM 值提交给 state,下一次 render 再用 value prop 控制表单。

React 流程图
React 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

组件关系与数据流

Form StateComponentBindingDOM Control

差异焦点模板只声明绑定目标,事件读取和属性回写由编译结果完成。

实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。

flowchart TB
  accTitle: Vue v-model 表单双向绑定
  accDescr: v-model 编译为表单属性绑定和更新事件,用户输入时写入 ref,响应式更新又把最新值同步到表单与预览模板。
  input[("input / select DOM")] <--> model{{"v-model 编译绑定"}}
  model <--> state(["name / direction refs"])
  state --> trigger{{"响应式依赖更新"}}
  trigger --> template["表单 + 预览模板"]
  class state state
  class template component
  class model,trigger process
  class input resource
  classDef state fill:#fffdf8,stroke:#2f9d6b,color:#25221d,stroke-width:1.2px
  classDef component fill:#e4f4eb,stroke:#2f9d6b,color:#25221d,stroke-width:2px,font-weight:600
  classDef process fill:#f2f8f4,stroke:#2f9d6b,color:#25221d,stroke-width:1.6px
  classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4px

v-model 组合属性绑定和更新事件,把 DOM 控件与响应式 ref 连接起来。

Vue 3 流程图
Vue 3 组件关系与数据流

滚轮缩放 · 按住拖动 · 双击重置 · Esc 关闭

完整 Demo 源码

以下代码直接读取在线 Demo 使用的源文件,没有省略或改写。

React 源码

ReactDemo.tsx
import { useState } from "react";import type { SubmitEvent } from "react";export function ReactDemo() {  const [name, setName] = useState("小明");  const [direction, setDirection] = useState("React");  const [submitted, setSubmitted] = useState(0);  function submit(event: SubmitEvent<HTMLFormElement>) {    event.preventDefault();    setSubmitted((current) => current + 1);  }  return (    <div className="demo-stack">      <form className="demo-form" onSubmit={submit}>        <label className="demo-field">          姓名          <input            className="demo-input"            value={name}            onChange={(event) => setName(event.target.value)}          />        </label>        <label className="demo-field">          学习方向          <select            className="demo-select"            value={direction}            onChange={(event) => setDirection(event.target.value)}          >            <option>React</option>            <option>Vue</option>            <option>两者对比</option>          </select>        </label>        <button className="demo-button" type="submit">          保存资料        </button>      </form>      <div className="demo-card">        <span className="demo-tag">实时 state</span>        <h2>{name || "未填写姓名"}</h2>        <p>正在学习:{direction}</p>        <p>已保存:{submitted} 次</p>      </div>    </div>  );}

Vue 3 源码

VueDemo.vue
<script setup lang="ts">import { ref } from "vue";const name = ref("小明");const direction = ref("Vue");const submitted = ref(0);function submit() {  submitted.value += 1;}</script><template>  <div class="demo-stack">    <form class="demo-form" @submit.prevent="submit">      <label class="demo-field">        姓名        <input v-model="name" class="demo-input" />      </label>      <label class="demo-field">        学习方向        <select v-model="direction" class="demo-select">          <option>React</option>          <option>Vue</option>          <option>两者对比</option>        </select>      </label>      <button class="demo-button" type="submit">保存资料</button>    </form>    <div class="demo-card">      <span class="demo-tag">实时 ref</span>      <h2>{{ name || "未填写姓名" }}</h2>      <p>正在学习:{{ direction }}</p>      <p>已保存:{{ submitted }} 次</p>    </div>  </div></template>

在线观察

两个表单都实时展示姓名和学习方向。

React

实时 state

小明

正在学习:React

已保存:0 次

Vue 3

实时 ref

小明

正在学习:Vue

已保存:0 次

常见边界

  • React 输入框在受控与非受控模式之间切换会产生警告;初始值应保持类型稳定。
  • v-model 不是不可解释的“魔法双向绑定”,组件上会展开为 prop 与 update:* 事件。
  • 表单值转换应放在输入边界,避免业务层同时处理字符串、数字和空值。
  • 大型 React 表单可用 React Hook Form 减少整棵表单的受控更新,见表单校验实践。