Files
home/docs/vue/component.md
2026-02-21 15:39:27 +08:00

252 lines
3.5 KiB
Markdown

# 组件开发
## 组件基础
### 定义组件
```vue
<!-- MyButton.vue -->
<template>
<button :class="['btn', `btn-${type}`]" @click="handleClick">
<slot></slot>
</button>
</template>
<script setup>
const props = defineProps({
type: {
type: String,
default: 'default',
validator: (value) => ['default', 'primary', 'danger'].includes(value)
}
})
const emit = defineEmits(['click'])
function handleClick(event) {
emit('click', event)
}
</script>
<style scoped>
.btn {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.btn-default {
background: #f0f0f0;
color: #333;
}
.btn-primary {
background: #42b883;
color: white;
}
.btn-danger {
background: #ff4d4f;
color: white;
}
</style>
```
### 使用组件
```vue
<template>
<MyButton type="primary" @click="handleButtonClick">
点击我
</MyButton>
</template>
<script setup>
import MyButton from './MyButton.vue'
function handleButtonClick() {
console.log('按钮被点击')
}
</script>
```
## Props
### Props声明
```vue
<script setup>
// 简单声明
const props = defineProps(['title', 'content'])
// 带类型的声明
const props = defineProps({
title: String,
content: {
type: String,
required: true
},
count: {
type: Number,
default: 0
},
items: {
type: Array,
default: () => []
}
})
</script>
```
### Props验证
```vue
<script setup>
const props = defineProps({
status: {
type: String,
required: true,
validator: (value) => ['active', 'inactive', 'pending'].includes(value)
},
callback: {
type: Function,
default: () => {}
}
})
</script>
```
## 事件
### 定义事件
```vue
<script setup>
const emit = defineEmits(['update', 'delete', 'change'])
function handleUpdate() {
emit('update', { id: 1, name: '更新数据' })
}
function handleDelete(id) {
emit('delete', id)
}
</script>
```
### v-model
```vue
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<!-- 使用 -->
<CustomInput v-model="text" />
```
## 插槽
### 默认插槽
```vue
<!-- Card.vue -->
<template>
<div class="card">
<slot></slot>
</div>
</template>
<!-- 使用 -->
<Card>
<h2>标题</h2>
<p>内容</p>
</Card>
```
### 具名插槽
```vue
<!-- Layout.vue -->
<template>
<div class="layout">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 使用 -->
<Layout>
<template #header>
<h1>页面标题</h1>
</template>
<p>主要内容</p>
<template #footer>
<p>页脚信息</p>
</template>
</Layout>
```
### 作用域插槽
```vue
<!-- List.vue -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<script setup>
defineProps(['items'])
</script>
<!-- 使用 -->
<List :items="users">
<template #default="{ item, index }">
<span>{{ index }}. {{ item.name }}</span>
</template>
</List>
```
## 生命周期
```vue
<script setup>
import { onMounted, onUpdated, onUnmounted } from 'vue'
onMounted(() => {
console.log('组件已挂载')
})
onUpdated(() => {
console.log('组件已更新')
})
onUnmounted(() => {
console.log('组件已卸载')
})
</script>
```