React和Vue中暴露子组件的属性和方法给父组件用,并且控制子组件暴露的颗粒度的做法

React

在 React 中,forwardRef 是一种高级技术,它允许你将 ref 从父组件传递到子组件,从而直接访问子组件的 DOM 节点或公开的方法。这对于需要操作子组件内部状态或 DOM 的场景非常有用。为了使子组件能够暴露其属性和方法给父组件,通常会结合 useImperativeHandle Hook 使用 forwardRef

如何使用 forwardRefuseImperativeHandle

  1. 创建一个带有 forwardRef 的子组件:

    • 使用 React.forwardRef 来创建一个接受 ref 参数的组件。
  2. 使用 useImperativeHandle 定义要暴露的方法和属性:

    • 在子组件中使用 useImperativeHandle 来定义哪些方法或属性应该通过 ref 暴露出去。
  3. 在父组件中使用 ref 来访问子组件的公开接口:

    • 创建一个 ref 并将其传递给子组件,然后通过这个 ref 访问子组件暴露的方法或属性。

示例代码

子组件 (ChildComponent.js)
jsx 复制代码
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  // 定义要暴露的方法
  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
      console.log('子组件的输入框获得了焦点');
    },
    getInputValue: () => inputRef.current.value,
  }));

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="这是子组件的输入框" />
    </div>
  );
});

export default ChildComponent;
父组件 (ParentComponent.js)
jsx 复制代码
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const childComponentRef = useRef(null);

  const handleFocus = () => {
    if (childComponentRef.current) {
      childComponentRef.current.focusInput();
    }
  };

  const handleGetValue = () => {
    if (childComponentRef.current) {
      const value = childComponentRef.current.getInputValue();
      console.log('子组件的输入值:', value);
    }
  };

  return (
    <div>
      <ChildComponent ref={childComponentRef} />
      <button onClick={handleFocus}>让子组件的输入框获得焦点</button>
      <button onClick={handleGetValue}>获取子组件的输入值</button>
    </div>
  );
};

export default ParentComponent;

解释

  • 子组件 (ChildComponent.js):

    • 使用 forwardRef 创建了一个接受 ref 参数的组件。
    • 使用 useImperativeHandle 定义了 focusInputgetInputValue 方法,并将它们绑定到传入的 ref 上。这意味着父组件可以通过 ref 访问这些方法。
  • 父组件 (ParentComponent.js):

    • 创建了一个 ref (childComponentRef) 并将其传递给 ChildComponent
    • 提供了两个按钮,分别用于调用子组件的 focusInputgetInputValue 方法。

这种方法确保了父组件可以安全地与子组件进行交互,同时保持良好的封装性。通过 useImperativeHandle,你可以精确控制哪些方法或属性是公开的,而不会意外地暴露不必要的实现细节。

Vue

当你通过 ref 获取到子组件的根 DOM 元素后,你可以使用标准的 DOM API 来访问或操作该元素及其子元素。如果你想要访问 <p> 标签,可以通过多种方式实现,具体取决于你想要进行的操作。

访问子元素的方法

  1. 使用 querySelectorquerySelectorAll:

    • 这些方法允许你根据选择器(如标签名、类名、ID 等)来查找特定的子元素。
  2. 遍历子节点:

    • 你可以使用 childrenchildNodes 或其他类似属性来遍历子节点。
  3. 直接访问特定子元素:

    • 如果你知道子元素的具体位置,可以直接通过 firstElementChildlastElementChild 等属性访问。

示例代码

假设你想在父组件中访问并打印子组件中的 <p> 标签的内容,可以按照以下方式修改你的代码:

子组件 (ChildComponent.vue)
vue 复制代码
<template>
  <div ref="root">
    <p id="content">这是子组件的内容</p>
  </div>
</template>

<script setup>
import { defineExpose, ref } from 'vue';

const root = ref(null);

// 使用 defineExpose 显式暴露给父组件的方法或属性
defineExpose({
  getRootEl: () => root.value,
});
</script>
父组件 (ParentComponent.vue)
vue 复制代码
<template>
  <ChildComponent ref="childComponent" />
  <button @click="handleClick">点击我</button>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childComponent = ref(null);

const handleClick = () => {
  if (childComponent.value) {
    // 调用子组件的公开方法来获取 DOM 引用
    const el = childComponent.value.getRootEl();
    
    // 使用 querySelector 查找 <p> 标签
    const pElement = el.querySelector('p#content');
    console.log('子组件的 <p> 内容:', pElement?.textContent);
  }
};
</script>

解释

  • 子组件 (ChildComponent.vue):

    • 我们为 <p> 标签添加了一个 id="content",以便更容易地通过 querySelector 查找它。
  • 父组件 (ParentComponent.vue):

    • handleClick 方法中,我们首先调用 getRootEl 获取子组件的根元素。
    • 然后,使用 querySelector 方法通过 ID 选择器查找 <p> 标签,并打印其文本内容。这里使用了可选链操作符 (?.) 来安全处理可能为 null 的情况。

这种方法确保了你能够以一种安全且可控的方式访问子组件内部的特定 DOM 元素。请记住,尽量减少对 DOM 的直接操作,除非确实有必要。保持尽可能多的逻辑在 Vue 的响应式系统内,这样可以使应用更加高效和易于维护。

相关推荐
飞天大河豚1 小时前
2025前端框架最新组件解析与实战技巧:Vue与React的革新之路
vue.js·react.js·前端框架
十步杀一人_千里不留行2 小时前
React Native 下拉选择组件首次点击失效问题的深入分析与解决
javascript·react native·react.js
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS医疗报销系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
道不尽世间的沧桑3 小时前
第9篇:插槽(Slots)的使用
前端·javascript·vue.js
B站计算机毕业设计超人3 小时前
计算机毕业设计SpringBoot+Vue.jst房屋租赁系统(源码+LW文档+PPT+讲解)
vue.js·spring boot·后端·eclipse·intellij-idea·mybatis·课程设计
bin91533 小时前
DeepSeek 助力 Vue 开发:打造丝滑的滑块(Slider)
前端·javascript·vue.js·前端框架·ecmascript·deepseek
杰九3 小时前
【环境配置】maven,mysql,node.js,vue的快速配置与上手
java·vue.js·spring boot·mysql·node.js·maven
GISer_Jing3 小时前
Node.js中如何修改全局变量的几种方式
前端·javascript·node.js
秋意钟4 小时前
Element UI日期选择器默认显示1970年解决方案
前端·javascript·vue.js·elementui
程序员黄同学5 小时前
请谈谈 Vue 中的 key 属性的重要性,如何确保列表项的唯一标识?
前端·javascript·vue.js