组件是Vue应用的基本构建块,允许我们将UI拆分成独立、可复用的部分。每个组件都有自己的模板、逻辑和样式。
<!-- MyButton.vue -->
<template>
<button @click="count++">
点击了 {{ count }} 次
</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<style scoped>
button {
background: #42b883;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
<!-- MyButton.vue -->
<template>
<button @click="count++">
点击了 {{ count }} 次
</button>
</template>
<script>
export default {
name: 'MyButton',
data() {
return {
count: 0
}
}
}
</script>
<!-- App.vue -->
<template>
<div>
<h1>我的应用</h1>
<MyButton />
<MyButton />
<MyButton />
</div>
</template>
<script setup>
import MyButton from './components/MyButton.vue'
</script>
Props用于父组件向子组件传递数据。
<!-- BlogPost.vue -->
<template>
<div class="blog-post">
<h3>{{ title }}</h3>
<p>{{ content }}</p>
<span>作者: {{ author }}</span>
</div>
</template>
<script setup>
// 定义props
const props = defineProps({
title: {
type: String,
required: true
},
content: {
type: String,
default: ''
},
author: {
type: String,
default: '匿名'
}
})
</script>
<!-- App.vue -->
<template>
<div>
<BlogPost
title="Vue3学习笔记"
content="Vue3是一个很棒的框架"
author="张三"
/>
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
:content="post.content"
:author="post.author"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import BlogPost from './components/BlogPost.vue'
const posts = ref([
{ id: 1, title: '文章1', content: '内容1', author: '作者1' },
{ id: 2, title: '文章2', content: '内容2', author: '作者2' }
])
</script>
<script setup>
defineProps({
// 基础类型检查
title: String,
likes: Number,
isPublished: Boolean,
tags: Array,
author: Object,
callback: Function,
// 多种可能的类型
propA: [String, Number],
// 必填的字符串
propB: {
type: String,
required: true
},
// 带有默认值的数字
propC: {
type: Number,
default: 100
},
// 带有默认值的对象
propD: {
type: Object,
default: () => ({ message: 'hello' })
},
// 自定义验证函数
propE: {
validator: (value) => {
return ['success', 'warning', 'danger'].includes(value)
}
}
})
</script>
子组件通过emit向父组件发送事件。
<!-- Counter.vue -->
<template>
<div>
<button @click="increment">增加</button>
<p>{{ count }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
// 定义emits
const emit = defineEmits(['increment', 'change'])
function increment() {
count.value++
emit('increment', count.value)
emit('change', { count: count.value, timestamp: Date.now() })
}
</script>
<!-- App.vue -->
<template>
<div>
<Counter
@increment="handleIncrement"
@change="handleChange"
/>
<p>父组件接收到的值: {{ parentCount }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Counter from './components/Counter.vue'
const parentCount = ref(0)
function handleIncrement(value) {
parentCount.value = value
console.log('计数增加到:', value)
}
function handleChange(data) {
console.log('变化数据:', data)
}
</script>
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<!-- 使用 -->
<template>
<CustomInput v-model="message" />
<p>{{ message }}</p>
</template>
<script setup>
import { ref } from 'vue'
import CustomInput from './components/CustomInput.vue'
const message = ref('')
</script>
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import MyButton from './components/MyButton.vue'
const app = createApp(App)
// 全局注册
app.component('MyButton', MyButton)
app.mount('#app')
<script setup>
import MyButton from './components/MyButton.vue'
// 导入后自动注册,可直接使用
</script>
<!-- UserCard.vue -->
<template>
<div class="user-card">
<img :src="avatar" :alt="name">
<h3>{{ name }}</h3>
<p>{{ bio }}</p>
<button @click="handleFollow">
{{ isFollowing ? '已关注' : '关注' }}
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
name: {
type: String,
required: true
},
avatar: {
type: String,
required: true
},
bio: {
type: String,
default: ''
}
})
const emit = defineEmits(['follow', 'unfollow'])
const isFollowing = ref(false)
function handleFollow() {
isFollowing.value = !isFollowing.value
emit(isFollowing.value ? 'follow' : 'unfollow', props.name)
}
</script>
<style scoped>
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
}
img {
width: 100px;
height: 100px;
border-radius: 50%;
}
</style>