252 lines
3.5 KiB
Markdown
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>
|
|
```
|