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>

Vue3语法知识小结_默认值

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>

Vue3语法知识小结_Text_02