ToolkitX
知识库工具箱

组件设计

props, emits, slots, provide/inject

25min·进阶

01. 组件的本质

组件就是把页面拆成一个个小积木块,每个积木块有自己独立的结构、样式和行为。Vue 组件文件以 .vue 结尾,里面分三块:template 写 HTML,script 写 JS,style 写 CSS。就像一个封装好的小盒子,对外暴露 props 接收数据,通过 emit 往外传消息。
html
<template>
  <button @click="handleClick">{{ label }}</button>
</template>

<script setup>
const props = defineProps({ label: String })
const emit = defineEmits(['click'])
function handleClick() { emit('click') }
</script>

<style scoped>
button { padding: 8px 16px; }
</style>

02. Props 父传子

Props 是父组件传给子组件的数据通道,只读不能改。在子组件里用 defineProps 定义接收哪些 prop,可以指定类型、默认值、是否必填。类型检查是在运行时做的,TypeScript 项目里还可以用泛型获得编译时检查。
javascript
// 子组件
const props = defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 }
})

// 父组件
<Card title="我的卡片" :count="10" />
Props 命名在模板里用 kebab-case,在 js 里用 camelCase。

03. Emits 子传父

子组件要通知父组件发生了什么事,就用 emit 发射事件。跟小孩按门铃一样,按一下门就响了,父组件听到门铃就知道该开门了。在子组件里用 defineEmits 声明可能发射的事件,然后调用 emit 函数。
javascript
// 子组件
const emit = defineEmits(['delete', 'update'])
emit('delete', itemId)

// 父组件监听
<Item @delete="handleDelete" @update="handleUpdate" />

04. 插槽 Slots

插槽就是组件里挖一个洞,让父组件往里面塞任意内容。默认插槽用一个 slot 标签占位,具名插槽用 name 区分多个洞。作用域插槽可以让子组件把数据传回给父组件使用,表格组件经常这么干,让父组件自定义每列怎么渲染。
html
// 子组件 Card
<template>
  <div class="card">
    <slot name="header" />
    <slot />
    <slot name="footer" />
  </div>
</template>

// 父组件使用
<Card>
  <template #header><h1>标题</h1></template>
  这是内容
  <template #footer><button>确定</button></template>
</Card>

05. 组件注册和动态组件

组件注册有全局和局部两种。全局注册在 main.js 里 app.component 一下,任何地方都能用但打包时没法 tree-shaking。局部注册就是在哪个文件用就在哪个文件 import,推荐这种方式。动态组件用 component 标签配合 is 属性,根据条件切换显示哪个组件,配合 keep-alive 可以缓存组件状态。
javascript
// 局部注册 - 推荐
import MyModal from '@/components/MyModal.vue'

// 动态组件
<component :is="currentTab" />

// 带缓存
<keep-alive><component :is="currentTab" /></keep-alive>
keep-alive 适合 Tab 切换场景,切回来不用重新渲染。

知识测验

1/4正确 0

.vue 文件里包含哪三部分?