跳转到内容

useRef 与模板 ref

父组件控制子组件内部输入框:聚焦和全选都调用真实 DOM 方法,两边分别展示如何拿到这条命令式通道。

React

forwardRef 把子组件的命令式能力交给父组件。

在线预览

等待操作

组件关系与数据流

ComponentRef / DOM

差异焦点函数组件不会自动暴露实例,需要 forwardRef 和 useImperativeHandle。

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

flowchart TB
  accTitle: React forwardRef 命令式句柄
  accDescr: SearchInput 必须通过 forwardRef 接收 ref,再用 useImperativeHandle 构造受控句柄;ReactDemo 调用句柄时最终操作 input DOM。
  parent["ReactDemo"] -->|"ref prop"| child["SearchInput + forwardRef"]
  child --> expose{{"useImperativeHandle 构造句柄"}}
  expose --> handle[("SearchInputHandle")]
  parent -.->|"focus() / select()"| handle
  handle --> dom[("input DOM")]
  class parent,child component
  class expose process
  class handle,dom resource
  classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
  classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

forwardRef 接收 ref,useImperativeHandle 显式构造父组件可调用的句柄。

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

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

实际运行源码

import { forwardRef, useImperativeHandle, useRef, useState } from "react";
type SearchInputHandle = {
focus: () => void;
select: () => void;
};
const SearchInput = forwardRef<SearchInputHandle>(function SearchInput(_, ref) {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
select: () => inputRef.current?.select(),
}), []);
return (
<label className="demo-field">
子组件输入框
<input
ref={inputRef}
className="demo-input"
defaultValue="React forwardRef"
/>
</label>
);
});
export function ReactDemo() {
const searchRef = useRef<SearchInputHandle>(null);
const [action, setAction] = useState("等待操作");
return (
<div className="demo-stack">
<SearchInput ref={searchRef} />
<div className="demo-actions">
<button
className="demo-button"
type="button"
onClick={() => {
searchRef.current?.focus();
setAction("已聚焦输入框");
}}
>
聚焦
</button>
<button
className="demo-button"
type="button"
onClick={() => {
searchRef.current?.select();
setAction("已全选输入内容");
}}
>
全选
</button>
</div>
<p className="demo-result" aria-live="polite">{action}</p>
</div>
);
}
Vue 3

模板 ref 获取子组件,defineExpose 限定可调用方法。

在线预览

等待操作

组件关系与数据流

ComponentRef / DOM

差异焦点模板 ref 直接获得组件公开实例,defineExpose 决定暴露边界。

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

flowchart TB
  accTitle: Vue template ref 与 defineExpose
  accDescr: VueDemo 使用模板 ref 获取子组件公开实例,SearchInput 通过 defineExpose 限定公开方法,方法内部再操作 input 模板 ref。
  child["SearchInput"] --> expose{{"defineExpose 限定公开方法"}}
  expose --> instance[("组件公开实例")]
  parent["VueDemo"] -.->|"template ref"| instance
  instance -->|"focus() / select()"| inputRef[("input template ref")]
  inputRef --> dom[("input DOM")]
  class parent,child component
  class expose process
  class instance,inputRef,dom resource
  classDef state fill:#ffffff,stroke:#42b883,color:#172033,stroke-width:1.2px
  classDef component fill:#e7f8f0,stroke:#42b883,color:#172033,stroke-width:2px,font-weight:600
  classDef process fill:#f2fbf7,stroke:#42b883,color:#172033,stroke-width:1.6px
  classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4px

VueDemo → template ref → defineExpose → SearchInput → input DOM

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

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

实际运行源码

<script setup lang="ts">
import { ref } from "vue";
import SearchInput from "./SearchInput.vue";
const searchRef = ref<InstanceType<typeof SearchInput> | null>(null);
const action = ref("等待操作");
function focusInput() {
searchRef.value?.focus();
action.value = "已聚焦输入框";
}
function selectInput() {
searchRef.value?.select();
action.value = "已全选输入内容";
}
</script>
<template>
<div class="demo-stack">
<SearchInput ref="searchRef" />
<div class="demo-actions">
<button class="demo-button" type="button" @click="focusInput">
聚焦
</button>
<button class="demo-button" type="button" @click="selectInput">
全选
</button>
</div>
<p class="demo-result" aria-live="polite">{{ action }}</p>
</div>
</template>