在 Vue 3 中,setup 函数是 Vue 3 中的 Composition API 的核心,它提供了一种更灵活的方式来定义组件逻辑。props 和 emit 是父子组件通信的基础,通过 setup 函数,这些机制得到了更简化的实现。Vue 3 中的 emits 事件管理也变得更加结构化。
一、setup 下的 props 和 emit
在 setup 函数中,props 是以参数的形式传递给组件,而 emit 事件函数也是通过参数获取的。我们来看看如何使用它们。
1. 父组件传递 props
在 Vue 3 中,父组件通过 props 向子组件传递数据的方式没有改变,仍然是在子组件标签上使用绑定的方式传递数据。
父组件代码:
<template>
  <div>
    <h1>父组件</h1>
    <ChildComponent :message="parentMessage" :count="parentCount" @child-event="handleChildEvent"></ChildComponent>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
  components: { ChildComponent },
  data() {
    return {
      parentMessage: 'Hello from parent!',
      parentCount: 10
    };
  },
  methods: {
    handleChildEvent(payload) {
      console.log('父组件接收到的子组件事件数据:', payload);
    }
  }
};
</script>
在这个例子中,父组件传递了 message 和 count 给子组件。
2. 子组件接收 props 和触发 emit
在 setup 函数中,props 和 emit 通过参数接收。emit 可以触发自定义事件,将数据传递给父组件。
子组件代码:
<template>
  <div>
    <h2>子组件</h2>
    <p>Message from parent: {{ message }}</p>
    <p>Count from parent: {{ count }}</p>
    <button @click="sendMessage">向父组件发送事件</button>
  </div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    message: String,
    count: Number,
  },
  setup(props, { emit }) {
    const sendMessage = () => {
      emit('child-event', 'Hello from Child');
    };
    return { props, sendMessage };
  }
});
</script>
在 setup 函数中,我们通过参数接收了 props,并定义了一个 sendMessage 函数,通过 emit 触发自定义事件 child-event,将数据传递给父组件。
二、emits 事件管理
在 Vue 3 中,emits 选项允许我们显式地定义子组件会触发哪些事件,这是一种增强的事件管理方式。
1. emits 选项
在子组件中,我们可以通过 emits 选项来定义组件可以触发的事件。这样做的好处是,可以对事件进行更好的文档化和管理,也可以对事件触发进行校验。
<script>
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    message: String,
    count: Number,
  },
  emits: ['child-event'],  // 定义子组件将会触发的事件
  setup(props, { emit }) {
    const sendMessage = () => {
      emit('child-event', 'Hello from Child');  // 触发事件
    };
    return { props, sendMessage };
  }
});
</script>
2. 事件校验
emits 选项不仅能定义事件,还可以为事件添加校验逻辑。我们可以通过函数校验事件的参数是否符合预期。
<script>
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    message: String,
    count: Number,
  },
  emits: {
    'child-event': (payload) => {
      return typeof payload === 'string';  // 校验事件参数必须是字符串
    }
  },
  setup(props, { emit }) {
    const sendMessage = () => {
      emit('child-event', 'Hello from Child');
    };
    return { props, sendMessage };
  }
});
</script>
在这个示例中,emits 选项中为 child-event 事件定义了一个校验函数,只有当事件参数是字符串时,事件才会被触发。
三、setup 下的原理解析
1. props 原理
在 setup 中接收的 props 是响应式的,这意味着当父组件传递的 props 数据变化时,子组件会自动更新。
- 在 
setup中接收到的props是不可直接解构的,否则将失去响应式能力。如果需要解构,可以使用toRefs或toRef。 
import { toRefs } from 'vue';
setup(props) {
  const { message, count } = toRefs(props);  // 解构成响应式变量
  return { message, count };
}
2. emit 原理
emit 函数是 Vue 内置的事件触发机制,它允许组件触发自定义事件,并将数据传递给父组件。Vue 内部会根据 emits 选项确保事件是有效的并调用父组件的监听器。
四、示例:父子组件通信
我们通过一个简单的例子,展示 props 和 emit 在 setup 下的具体应用。
父组件:
<template>
  <div>
    <h1>父组件</h1>
    <p>Count: {{ parentCount }}</p>
    <ChildComponent :count="parentCount" @update-count="handleUpdateCount"></ChildComponent>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
  components: { ChildComponent },
  data() {
    return {
      parentCount: 0
    };
  },
  methods: {
    handleUpdateCount(newCount) {
      this.parentCount = newCount;
    }
  }
};
</script>
子组件:
<template>
  <div>
    <h2>子组件</h2>
    <p>当前计数: {{ count }}</p>
    <button @click="increment">增加计数</button>
  </div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    count: Number,
  },
  emits: ['update-count'],
  setup(props, { emit }) {
    const increment = () => {
      emit('update-count', props.count + 1);
    };
    return { increment };
  }
});
</script>
总结
props:setup函数中props是通过参数传递的,不能直接解构,否则会丧失响应性。emit:emit函数可以触发事件,父组件通过监听子组件的自定义事件来实现双向数据交互。emits:在 Vue 3 中,可以通过emits选项来显式定义组件的事件,并且可以进行参数校验。
Vue 3 的 setup 和 emits 机制使组件通信变得更加简洁和灵活,同时也增强了代码的可读性和可维护性。