父子组件传值
父组件通过v-bind绑定一个数据,然后子组件通过defineProps接受传过来的值
<template>
<div class="layout">
<Menu v-bind:data="data" title="我是标题"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
</script>
- 如上述代码,给Menu组件 传递了一个
title字符串类型是不需要v-bind - 传递非字符串类型需要加v-bind 简写 冒号
子组件接受值
- 通过defineProps 来接受 defineProps是无须引入的直接使用即可(setup语法糖)
- 如果我们使用的
TypeScript可以使用传递字面量类型的纯类型语法做为参数
<template>
<div class="menu">
菜单区域 父组件传值:{{ title }}
<div>父组件传值:{{ data }}</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title:string,
data:number[]
}>()
</script>
或者
<template>
<div class="menu">
菜单区域 父组件传值:{{ title }}
<div>父组件传值:{{ data }}</div>
</div>
</template>
<script setup lang="ts">
type Props = {
title: string,
data: number[]
}
defineProps<Props>()
</script>
- TS 特有的默认值方式
withDefaults是个函数也是无须引入开箱即用接受一个props函数第二个参数是一个对象设置默认值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: "张三",
data: () => [1, 2, 3]
})
如果你使用的不是TS,还是vue2的写法
defineProps({
title:{
default:"",
type:string
},
data:Array
})
子组件向父组件传值是通过defineEmits派发一个事件
- 我们在子组件绑定了一个click 事件 然后通过defineEmits 注册了一个自定义事件
- 点击click 触发 emit 去调用我们注册的事件 然后传递参数
<template>
<div class="menu">
菜单区域 父组件传值:{{ title }}
<div>父组件传值:{{ data }}</div>
<div>
<button @click="handleClick">派发</button>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([1,1,1])
type Props = {
title: string,
data: number[]
}
// 接收父组件传值
defineProps<Props>()
// 子组件向父组件传值
/**
* @description:
* @params [自定义事件名称]
* @return {*}
*/
const emit = defineEmits(['selfClick'])
const handleClick = () => {
emit('selfClick', list, false)
}
</script>
父组件
<template>
<div class="layout">
<Menu @selfClick="getList" v-bind:data="data" title="我是标题"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
const getList = (list:number[], flag:number) => {
console.log(list, '我是子组件中传递过来的值', flag)
}
</script>
获取子组件实例
<template>
<div class="layout">
<Menu ref="menus" @selfClick="getList" v-bind:data="data" title="我是标题"></Menu>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
const menus = ref(null) // menus与定义的ref值一样
console.log(menus.value) // 获取子组件实例
// 然后打印menus.value 发现没有任何属性
</script>
- 这时候父组件想要读到子组件的属性可以在子组件中通过
defineExpose暴露,defineExpose是个函数也是无须引入开箱即用 - 在子组件中
const list = reactive<number[]>([1,1,1])
// 子组件向外暴露
defineExpose({
list
})
父组件接收
type SonList = {
list: number[],
}
let sonList = menus.value as unknown as SonList
/*
在开发中经常会遇到类型定义的不太好,需要用 as 进行断言的情况,简单来看,可以直接用 as any 解决几乎所有的 ts 类型问题
但不利于后续的维护,维护者可能并不知道被 as any 的目标应该是什么类型,用 as unknown as 代替可以解决该问题,而且能看到明确的类型和具体的格式
*/
console.log(sonList.list, '打印子组件实例上向外暴露的值') // 获取子组件实例
配置递归组件
原理跟我们写js递归是一样的 自己调用自己 通过一个条件来结束递归 否则导致内存泄漏
- 案例递归树
- 在父组件配置数据结构 数组对象格式 传给子组件
<template>
<div>
<Tree @selfClick="getList" :data="data"></Tree>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import Tree from './components/Tree/index.vue'
type TreeList = {
name: string;
icon?: string;
children?: TreeList[] | [];
}
const data = reactive<TreeList[]>([
{
name: "no.1",
children: [
{
name: "no.1-1",
children: [
{
name: "no.1-1-1",
},
],
},
],
},
{
name: "no.2",
children: [
{
name: "no.2-1",
},
],
},
{
name: "no.3",
},
])
const getList = (items: TreeList) => {
console.log(items, '子组件传递过来的值')
}
</script>
子组件接收值
- TreeItem 其实就是当前组件自身 通过import 把自身又引入了一遍 如果他没有children 了就结束
<template>
<div style="padding-left:10px;" class="tree">
<div style="text-align:left;" :key="index" v-for="(item,index) in data">
<div @click.stop='clickItem(item)'>{{item.name}}
</div>
<!-- 向组件内部派发selfClick -->
<TreeItem @selfClick='clickItem' v-if='item?.children?.length' :data="item.children"></TreeItem>
</div>
</div>
</template>
<script setup lang="ts">
// import TreeItem from './index.vue'
type TreeList = {
name: string;
icon?: string;
children?: TreeList[] | [];
}
type Props<T> = {
data?:T[] | []
}
defineProps<Props<TreeList>>()
const emit = defineEmits(['selfClick'])
const clickItem = (item: TreeList) => {
// console.log(item, '222')
emit('selfClick', item)
}
</script>
<!-- 不带setup -->
<script lang="ts">
// 向外暴露组件名字
export default {
name: "TreeItem"
}
</script>
效果