React render 与 Vue setup
React 函数组件会随状态更新重新执行;Vue setup 通常只在组件创建时执行一次。
核心结论
React 函数组件本身就是一次 render:状态更新后,组件函数会重新执行并计算下一棵 UI。Vue 的 setup 更像组件实例的初始化入口,通常只执行一次,后续由响应式依赖触发模板更新。
不要把执行次数等同于 DOM 重建
React 重新执行组件函数,不代表整棵真实 DOM 都会重建;Vue 更新模板,也不代表完全不会重新运行组件的更新函数。两边最终都会比较并提交必要的 DOM 变化。
更新流程
组件关系与数据流
StateComponentRender / SetupHandler
差异焦点组件函数是 render 过程,不是只运行一次的初始化函数。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: React 函数组件重新执行模型
accDescr: 事件调用 setter 后更新 state,React 调度一次新的 render,重新执行 ReactDemo 函数并生成下一棵 UI。
event{{"点击事件"}} --> setter{{"调用 state setter"}}
setter --> state(["count / compact state"])
state --> schedule{{"调度新的 render"}}
schedule --> demo["重新执行 ReactDemo"]
demo -.->|"创建下一次事件处理函数"| event
class state state
class demo component
class event,setter,schedule process
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任意本地 state 更新都会调度 render,ReactDemo 函数从头再次执行。
组件关系与数据流
StateComponentRender / SetupHandler
差异焦点状态变化不会让 setup 从头执行,已有闭包和响应式引用继续复用。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: Vue setup 与响应式更新模型
accDescr: setup 创建 refs 和事件函数一次,点击后直接修改 ref,响应式系统只调度读取该值的模板更新。
setup{{"setup 创建一次"}} --> state(["count / compact refs"])
setup --> handlers[("稳定的事件函数")]
handlers -.->|"直接修改 ref"| state
state --> trigger{{"触发响应式依赖"}}
trigger --> template["更新相关模板节点"]
class state state
class template component
class setup,trigger process
class handlers 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.4pxsetup 保留 refs 和事件函数,状态变化后按响应式依赖更新模板。
完整 Demo 源码
以下代码直接读取在线 Demo 使用的源文件,没有省略或改写。
React 源码
import { useRef, useState } from "react";export function ReactDemo() { const executions = useRef(0); const [count, setCount] = useState(0); const [compact, setCompact] = useState(false); executions.current += 1; return ( <div className="demo-stack"> <div className="demo-card"> <span className="demo-tag">函数组件执行</span> <h2>{compact ? "紧凑视图" : "标准视图"}</h2> <p>count:{count}</p> <p>ReactDemo 本次挂载已执行:{executions.current} 次</p> </div> <div className="demo-actions"> <button className="demo-button" type="button" onClick={() => setCount((current) => current + 1)} > count + 1 </button> <button className="demo-button" type="button" onClick={() => setCompact((current) => !current)} > 切换显示模式 </button> </div> </div> );}Vue 3 源码
<script setup lang="ts">import { ref } from "vue";const setupExecutions = 1;const count = ref(0);const compact = ref(false);</script><template> <div class="demo-stack"> <div class="demo-card"> <span class="demo-tag">setup 执行</span> <h2>{{ compact ? "紧凑视图" : "标准视图" }}</h2> <p>count:{{ count }}</p> <p>VueDemo 本次挂载已执行 setup:{{ setupExecutions }} 次</p> </div> <div class="demo-actions"> <button class="demo-button" type="button" @click="count += 1"> count + 1 </button> <button class="demo-button" type="button" @click="compact = !compact"> 切换显示模式 </button> </div> </div></template>在线观察
两个 Demo 都维护计数和显示模式,并记录逻辑执行次数。分别修改两个状态,观察 React render 次数与 Vue setup 次数。
React
函数组件执行
标准视图
count:0
ReactDemo 本次挂载已执行:1 次
Vue 3
setup 执行
标准视图
count:0
VueDemo 本次挂载已执行 setup:1 次
选择心智模型
| 问题 | React | Vue 3 |
|---|---|---|
| 组件逻辑何时运行 | 每次 render 执行函数组件 | 创建实例时执行 setup |
| 状态如何推动更新 | setter 调度新的 render | ref/reactive 触发依赖它的 effect |
| 事件函数是否重建 | render 时重新创建 | 通常由 setup 创建并复用 |
继续阅读响应式系统与状态更新模型理解“框架如何知道要更新”,再到状态 API学习日常写法。