子传父的核心应用场景(vue3) - 教程

我们常见的组件通信的方式是prop和emit,下面我们来探讨一下什么场景下使用子传父。

子传父的核心场景是:子组件产生了“需要父组件响应”的事件或数据,但子组件自身无法(或不应该)处理,必须交给父组件决策/存储/分发

父传子是“数据下行”(父给子展示用),子传父是“事件/数据上行”(子告诉父“我发生了什么”,让父处理)。以下是最常见的场景,带具体例子和简单实现思路:

一、最核心场景:子组件的用户交互,需要父组件处理逻辑

子组件是“交互载体”(比如按钮、输入框、表单),用户操作子组件后,子组件本身不知道要做什么业务逻辑,必须通知父组件执行。

1. 按钮/开关的“触发事件”
  • 场景:子组件是一个自定义按钮(比如“提交按钮”“删除按钮”),用户点击后,触发了子组件的事件:如提交表单、删除操作等,但是子组件不知道要提交什么、删除什么,需要父组件执行提交API、删除数据等逻辑。
  • 例子
    • 子组件(MyButton):只负责渲染按钮,点击时触发父组件传来的回调。
    • 父组件:接收点击事件,执行“提交表单”的逻辑。
  • 简单代码
<!-- 子组件 MyButton.vue -->
  <template>
  <button @click="handleClick"> {{ btnText }} </button>
  </template>
    <script setup>
    import { defineProps, defineEmits } from 'vue';
    const props = defineProps({
    btnText: {
    type: String,
    required: true
    }
    });
    const emit = defineEmits(['click']);
    const handleClick = () => {
    emit('click');
    };
  </script>
  <!-- 父组件 Parent.vue -->
    <template>
      <MyButton btnText="提交" @click="submitForm" />
    </template>
      <script setup>
      import MyButton from './MyButton.vue';
      const submitForm = () => {
      // 调用API提交数据、跳转页面等操作
      };
    </script>
2. 输入框/选择器的“数据回传”
  • 场景:子组件是一个自定义输入框(比如“搜索输入框”“日期选择器”),用户输入/选择后,子组件需要把输入的值传给父组件,父组件可能用这个值做搜索、筛选、存储等。
  • 例子
    • 子组件(MyInput):负责输入框渲染,输入时把值传给父组件。
    • 父组件:接收输入值,执行“搜索数据”的逻辑。
  • 简单代码
<!-- 子组件 MyInput.vue -->
  <template>
    <input
      type="text"
      :value="modelValue"
      @input="handleInput"
      placeholder="请输入搜索关键词"
    />
  </template>
    <script setup>
    import { defineProps, defineEmits } from 'vue';
    const props = defineProps({
    modelValue: {
    type: String,
    default: ''
    }
    });
    const emit = defineEmits(['update:modelValue']);
    const handleInput = (e) => {
    const inputValue = e.target.value;
    emit('update:modelValue', inputValue);
    };
  </script>
  <!-- 父组件 Parent.vue -->
    <template>
      <MyInput v-model="searchKey" />
    <button @click="searchData">搜索</button>
    <p>搜索关键词: {{ searchKey }}</p>
    </template>
      <script setup>
      import { ref } from 'vue';
      import MyInput from './MyInput.vue';
      const searchKey = ref('');
      const searchData = () => {
      // 调用API搜索数据...
      };
    </script>
3. 表单提交的“数据汇总”
  • 场景:子组件是一个完整表单(比如“用户注册表单”“商品编辑表单”),用户填写完成后点击提交,子组件收集所有表单字段的值,传给父组件,父组件处理API提交、数据校验等。
  • 例子
    • 子组件(UserForm):收集姓名、年龄等字段,提交时把表单数据传给父。
    • 父组件:接收表单数据,执行“注册用户”的API请求。

二、子组件状态变化,需要父组件同步

子组件自身管理了一些状态(比如折叠/展开、选中状态),但父组件需要知道这个状态(比如父组件的标题要根据子组件的折叠状态变化,或父组件要根据子组件的选中状态更新其他UI)。

1. 折叠面板/抽屉的“状态同步”
  • 场景:子组件是一个折叠面板(比如“筛选条件面板”),用户点击展开/收起时,子组件需要把“是否展开”的状态传给父组件,父组件可能要显示/隐藏其他元素(比如“展开时显示重置按钮,收起时隐藏”)。
  • 简单代码
<!-- 子组件 CollapsePanel.vue -->
  <template>
      <div class="panel">
        <div class="header" @click="toggleCollapse">
        {{ title }}
      <span>{{ isCollapsed ? '展开' : '收起' }}</span>
      </div>
        <div class="content" v-show="!isCollapsed">
        面板内容...
      </div>
    </div>
  </template>
    <script setup>
    import { ref, defineProps, defineEmits } from 'vue';
    const props = defineProps({
    title: {
    type: String,
    required: true
    },
    collapsed: {
    type: Boolean,
    required: true
    }
    });
    const emit = defineEmits(['update:collapsed']);
    const isCollapsed = ref(props.collapsed);
    // 监听 props 变化,同步到内部状态
    watch(() => props.collapsed, (newVal) => {
    isCollapsed.value = newVal;
    });
    const toggleCollapse = () => {
    isCollapsed.value = !isCollapsed.value;
    emit('update:collapsed', isCollapsed.value);
    };
  </script>
  <!-- 父组件 Parent.vue -->
    <template>
      <CollapsePanel title="筛选条件" v-model:collapsed="isPanelCollapsed" />
    <button v-show="!isPanelCollapsed" @click="resetFilter">重置筛选</button>
    </template>
      <script setup>
      import { ref } from 'vue';
      import CollapsePanel from './CollapsePanel.vue';
      const isPanelCollapsed = ref(true);
      const resetFilter = () => {
      console.log('父组件:重置筛选条件');
      };
    </script>
2. 选项卡/分页的“切换事件”
  • 场景:子组件是一个分页组件(比如“商品列表分页”),用户点击“下一页”时,子组件需要把“当前页码”传给父组件,父组件根据页码请求对应页的数据。
  • 例子
    • 子组件(Pagination):管理页码、每页条数,页码变化时通知父。
    • 父组件:接收页码,请求对应页的商品数据。

三、子组件需要“反向控制”父组件的UI/数据

子组件的某个操作,需要父组件改变自身的状态(比如父组件的弹窗显示/隐藏、父组件的数据源更新)。

1. 弹窗的“关闭事件”
  • 场景:子组件是一个弹窗(比如“提示弹窗”“确认弹窗”),用户点击“关闭”或“取消”时,子组件需要通知父组件“我要关闭了”,父组件控制弹窗的v-showv-if状态。
  • 简单代码
<!-- 子组件 MyModal.vue -->
  <template>
      <div class="modal" v-if="modelValue">
        <div class="modal-content">
      <h3>弹窗标题</h3>
      <p>弹窗内容...</p>
      <button @click="handleClose">关闭</button>
      </div>
    </div>
  </template>
    <script setup>
    import { defineProps, defineEmits } from 'vue';
    const props = defineProps({
    modelValue: {
    type: Boolean,
    required: true
    }
    });
    const emit = defineEmits(['update:modelValue', 'close']);
    const handleClose = () => {
    emit('update:modelValue', false);
    emit('close');
    };
  </script>
  <!-- 父组件 Parent.vue -->
    <template>
    <button @click="showModal = true">打开弹窗</button>
      <!--
      使用 v-model 绑定弹窗的显示状态。
      同时监听 'close' 事件,执行额外的回调。
      -->
      <MyModal v-model="showModal" @close="onModalClose" />
    </template>
      <script setup>
      import { ref } from 'vue';
      import MyModal from './MyModal.vue';
      const showModal = ref(false);
      // 弹窗关闭时的回调
      const onModalClose = () => {
      };
    </script>
2. 子组件触发父组件的数据源更新
  • 场景:子组件是一个“新增用户”组件,用户填写完信息提交后,子组件把新增的用户数据传给父组件,父组件把这个数据添加到自己的“用户列表”数据源中,实现列表实时更新。

总结:子传父的核心逻辑

子组件是“执行者”(负责UI渲染和用户交互),父组件是“决策者”(负责业务逻辑、数据存储、全局状态管理)。当子组件发生了“自己处理不了”的事件或数据变化时,就需要通过$emit(Vue)、props回调(React)等方式“上报”给父组件,让父组件处理。

简单记:父传子是“给数据”,子传父是“报事件”

posted on 2026-02-02 14:07  ljbguanli  阅读(0)  评论(0)    收藏  举报