React × Vue

Context 与 provide / inject

两种方案都能绕过逐层传递,让相隔较远的组件共享并更新同一份上下文。

核心结论

Context 和 provide/inject 都用于把依赖提供给后代组件,而不是替代所有状态管理。React 消费者跟随 Provider 的 value 更新;Vue 通常提供一个响应式 ref 或 reactive 对象,消费者读取同一引用。

依赖注入关系

组件关系与数据流

StateComponentContext

差异焦点Provider 的 value 引用变化会推动 Context 消费者重新渲染。

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

flowchart TB
  accTitle: React Context Provider 更新机制
  accDescr: ReactDemo 每次渲染创建 Provider value 对象,value 身份变化后 useContext 消费者重新渲染,UserActions 通过 setUser 更新源状态。
  user(["user state"]) --> render{{"ReactDemo render 创建 value"}}
  render --> context[("UserContext.Provider value")]
  context --> profile["UserProfile useContext"]
  context --> actions["UserActions useContext"]
  actions -.->|"setUser"| user
  class user state
  class profile,actions component
  class render process
  class context 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

Provider value 身份随 render 更新,useContext 消费者接收新的上下文值。

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

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

组件关系与数据流

StateComponentContext

差异焦点共享的是响应式引用,不依赖 Provider value 的对象身份变化。

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

flowchart TB
  accTitle: Vue provide inject 响应式共享机制
  accDescr: setup 执行时 provide 一次包含 user ref 的上下文,inject 消费者读取同一个 ref,修改 ref 后只触发读取它的响应式视图更新。
  setup{{"setup 中 provide 一次"}} --> context[("userKey + user ref")]
  context --> profile["UserProfile inject"]
  context --> actions["UserActions inject"]
  actions -.->|"直接修改共享 ref"| user(["user ref"])
  user -->|"响应式依赖触发"| profile
  user --> actions
  class user state
  class profile,actions component
  class setup process
  class context 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

provide 传递同一个响应式 ref;inject 消费者按实际读取建立依赖。

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

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

完整 Demo 源码

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

React 源码

ReactDemo.tsx
import { createContext, useContext, useState } from "react";type User = {  name: string;  role: "访客" | "管理员";};type UserContextValue = {  user: User;  setName: (name: string) => void;  toggleRole: () => void;};const UserContext = createContext<UserContextValue | null>(null);function useUser() {  const context = useContext(UserContext);  if (!context) throw new Error("Missing UserProvider");  return context;}function UserProfile() {  const { user } = useUser();  return (    <div className="demo-card">      <span className="demo-tag">Consumer A</span>      <h2>{user.name}</h2>      <p>当前角色:{user.role}</p>    </div>  );}function UserActions() {  const { user, setName, toggleRole } = useUser();  return (    <div className="demo-form">      <label className="demo-field">        Consumer B 修改用户名        <input          className="demo-input"          value={user.name}          onChange={(event) => setName(event.target.value)}        />      </label>      <div className="demo-actions">        <button className="demo-button" type="button" onClick={toggleRole}>          切换角色        </button>      </div>    </div>  );}export function ReactDemo() {  const [user, setUser] = useState<User>({ name: "小明", role: "访客" });  const value: UserContextValue = {    user,    setName: (name) =>      setUser((current) => ({ ...current, name: name || "小明" })),    toggleRole: () =>      setUser((current) => ({        ...current,        role: current.role === "访客" ? "管理员" : "访客",      })),  };  return (    <UserContext.Provider value={value}>      <div className="demo-stack">        <UserProfile />        <UserActions />      </div>    </UserContext.Provider>  );}

Vue 3 源码

<script setup lang="ts">import { provide, ref } from "vue";import UserActions from "./UserActions.vue";import UserProfile from "./UserProfile.vue";import { userKey, type User } from "./userContext";const user = ref<User>({ name: "小明", role: "访客" });function setName(name: string) {  user.value.name = name || "小明";}function toggleRole() {  user.value.role = user.value.role === "访客" ? "管理员" : "访客";}provide(userKey, { user, setName, toggleRole });</script><template>  <div class="demo-stack">    <UserProfile />    <UserActions />  </div></template>

在线观察

资料卡与操作面板没有直接传值关系,但会读取并更新同一个用户上下文。

React

Consumer A

小明

当前角色:访客

Vue 3

Consumer A

小明

当前角色:访客

使用边界

  • 优先用 props 表达局部、明确的数据流;层级很深或依赖具有全局语义时再使用上下文。
  • React Provider 的对象 value 每次重建都可能推动消费者更新,可按实际性能问题拆分 Context 或稳定 value。
  • Vue 建议提供响应式引用,并把修改方法一起封装,避免消费者随意破坏不变量。
  • 缺少 Provider 时应尽早抛出有语义的错误,而不是让 undefined 在深层传播。

局部通信见Props / Callback 与 props / emit。