1、父子组件传值
(1)父组件给子组件传值
子组件
使用defineProps来接受来自父组件的值,可以定义类型和默认值
在模版中可以直接使用值名称,而在script中只能使用props.值名称来使用
<script setup lang="ts">
// 使用defineProps定义props,这样就可以在模板中使用props了
// js写法
const props = defineProps({
// 这里的name就是props的名称,类型为String,且默认值为"默认值"
name: {
type: String,
default: "默认值"
}
})
// ts写法
const props = defineProps<{
name: string
}>()
// 使用withDefaults,可以为props设置默认值
const props = withDefaults(defineProps<{
name: string
}>(),{
name: () => "默认值" // 需要使用函数返回默认值,防止默认值被共享
})
console.log(props.name) // 在setup中可以直接使用props
</script>
<template>
<div class="container">
<div class="child">{{ name }}</div>
</div>
</template>
<style scoped lang="scss">
</style>
父组件
在组件上使用 :值名称=值
<script setup lang="ts">
import Watch from "./components/Watch.vue";
const name: string = "hello";
</script>
<template>
<Watch :name="name"/>
</template>
(2)子组件给父组件传值
子组件
<script setup lang="ts">
const name = '我是子组件的数据'
const emit = defineEmits(['onEmit'])
const onEmit = () =>{
emit('onEmit', name)
}
</script>
<template>
<div class="container">
<div class="child">
<button @click="onEmit">提交给父组件</button>
</div>
</div>
</template>
<style scoped lang="scss">
</style>
父组件
<script setup lang="ts">
import Watch from "./components/Watch.vue";
const getName = (name: string) => {
console.log(name);
}
</script>
<template>
// 自定义事件使用 @事件名
<Watch @onEmit="getName"/>
</template>
2、全局组件
首先编写一个Search组件
<script setup lang="ts">
import { ref } from "vue";
const searchText = ref("");
const search = () => {
console.log(searchText.value);
}
</script>
<template>
<div>
<input type="text" v-model="searchText" />
<button @click="search">搜索</button>
</div>
</template>
<style scoped lang="scss">
</style>
在main.ts中注册组件为全局组件
// 引入组件
import Search from "./components/Search.vue";
const app = createApp(App)
// 注册组件
app.component('Search', Search)
app.mount('#app')
在其他组件中直接使用,无需引入
<template>
<Search />
</template>
3、动态组件
让多个组件使用同一个挂载点,并动态切换,这就是动态组件。
在挂载点使用component标签,然后使用v-bind:is=”组件”
适用于tab切换
<script setup lang="ts">
import A from './components/A.vue'
import B from './components/B.vue'
import {reactive, shallowRef, markRaw } from 'vue'
// 定义一个数组,用来存放组件的信息
const data = reactive([
{
id: 1,
name: 'A',
component: markRaw(A) // 使用markRaw标记为原始值,防止组件被优化
},
{
id: 2,
name: 'B',
component: markRaw(B)
}
])
const componentId = shallowRef(A) // 使用shallowRef创建一个浅的响应式引用
const changeComponent = (item: any) => {
componentId.value = item.component
}
</script>
<template>
<div class="tabs">
<button class="btn" v-for="item in data" @click="changeComponent(item)">{{ item.name }}组件</button>
</div>
<Component :is="componentId"></Component>
</template>
<style lang="scss">
// 注意,这里不需要使用scoped
#app{
@include bfc;
}
.tabs{
width: 160px;
height: 50px;
line-height: 50px;
}
.btn{
padding: 5px 10px;
border: 1px solid #eee;
}
</style>
4、插槽
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。
插槽分为:匿名插槽、具名插槽、作用域插槽、动态插槽
子组件
<script setup lang="ts">
</script>
<template>
<div>
<slot name="header"></slot>
<slot></slot>
<div>
<div v-for="item in 10">
<slot name="data" :data="item"></slot>
</div>
</div>
<slot name ="footer"></slot>
</div>
</template>
<style scoped lang="scss">
</style>
父组件,需要引入子组件A,在子组件中使用插槽
<script setup lang="ts">
import A from './components/A.vue'
const slotName = 'footer'
</script>
<template>
<A>
<template v-slot:header>
<div>header,具名插槽,需要使用名称,可以简写为 #header</div>
</template>
<template #default>
<div>匿名插槽,作为默认</div>
</template>
<template #data="{ data }">
<div>{{ data }}作用域插槽</div>
</template>
<template #[slotName]>
<div>动态插槽</div>
</template>
</A>
</template>