文章目录
一、vue3 父子组件传参
**
父组件向子组件传递参数
**
父组件通过v-bind绑定一个数据,然后子组件通过defineProps接受传过来的值。
字符串类型不需要v-bind
父组件中向子组件传递数据(字符串)的方法
<template>
<div class="container">
<div class="left-menu">
<!-- 重点在这里-->
<Menu title="这个字符串可以传到Menu这个组件中"></Menu>
</div>
<div class="right">
<Header class="header"></Header>
<Content class="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'
</script>
<style lang='scss' scoped>
.container{
display: flex;
.left-menu{
width: 20%;
background-color: yellowgreen;
}
.right{
width: 80%;
border:1px solid pink;
flex-flow: column wrap;
.header{
width:100%;
height: 200px;
border: 1px solid yellowgreen;
}
.content{
width: 100%;
height: 200px;
border: 1px solid burlywood;
}
}
}
</style>
子组件Menu.vue中接收父组件传递过来的字符串
<template>
<div class="menu">
左边菜单区域 <br>
{{title}}
</div>
</template>
<script setup lang='ts'>
// 接收来自父组件传递来的数据
// 传递过来的数据用一个类型约束一下
type Props = {
title:string
}
defineProps<Props>()
</script>
<style lang='scss' scoped>
</style>
从父组件传递复杂数据类型到子组件中
父组件代码
<template>
<div class="container">
<div class="left">
<!-- 这个:data可以写成v-bind:data -->
<Menu class="menu" title="将这个title传到Menu子组件中" :data="list"></Menu>
</div>
<div class="right">
<Header class="header"></Header>
<Content class="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 { ref, reactive } from 'vue'
const list = ref<number[]>([1, 2, 3])
</script>
<style lang='scss' scoped>
.container{
display: flex;
flex-flow: row wrap;
.left{
width: 20%;
.menu{
background-color: rebeccapurple;
}
}
.right{
width: 80%;
.header{
width: 100%;
height: 300px;
background-color: palevioletred;
}
.content{
width: 100%;
height: 300px;
background-color: yellowgreen;
}
}
}
</style>
子组件代码
<template>
<div>
左侧菜单区域 <br>
{{title}} <br>
{{data}}
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
type Props = {
title: string,
// 接收父组传递过来数组数据,key要和v-bind:data一致
data: number[]
}
defineProps<Props>()
</script>
<style lang='scss' scoped>
</style>
子组件向父组件传递参数
子组件通过事件向父组件传递参数
子组件
<template>
<div>
左侧菜单区域 <br>
<button @click="clickTap">派发事件</button>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const list = reactive<number[]>([8, 8, 8])
// defineEmits() 自定义一个事件,这个事件在父组件中可以拿到,返回一个函数
const emit = defineEmits(['on-click'])
const clickTap = ()=>{
// 通过这个函数参数将数据传到父组件中
emit('on-click', list)
}
</script>
<style lang='scss' scoped>
</style>
父组件
<template>
<div class="container">
<div class="left">
<!-- 这个:data可以写成v-bind:data -->
<!-- on-click是子组件传递过来自定义事件,搞一个函数接收,这个函数中携带子组件传过来的数据 -->
<Menu class="menu" title="将这个title传到Menu子组件中" :data="list" @on-click="getList"></Menu>
</div>
<div class="right">
<Header class="header"></Header>
<Content class="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 { ref, reactive } from 'vue'
const list = ref<number[]>([1, 2, 3])
// 接收子组件传递过来的数据
const getList = (list:number[])=>{
console.log(list, '子组件传递过来的list')
}
</script>
<style lang='scss' scoped>
.container{
display: flex;
flex-flow: row wrap;
.left{
width: 20%;
.menu{
background-color: rebeccapurple;
}
}
.right{
width: 80%;
.header{
width: 100%;
height: 300px;
background-color: palevioletred;
}
.content{
width: 100%;
height: 300px;
background-color: yellowgreen;
}
}
}
</style>
父组件中拿子组件实例
父组件
<template>
<div class="container">
<div class="left">
<!-- 通过ref="menu"来拿子组件的实例 -->
<Menu class="menu" ref="menu" ></Menu>
</div>
<div class="right">
<Header class="header"></Header>
<Content class="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 { ref, onMounted } from 'vue'
const menu = ref(null)
onMounted(()=>{
// 如果子组件不暴躁数据出来,你什么都拿不到
console.log(menu.value)
})
</script>
<style lang='scss' scoped>
.container{
display: flex;
flex-flow: row wrap;
.left{
width: 20%;
.menu{
background-color: rebeccapurple;
}
}
.right{
width: 80%;
.header{
width: 100%;
height: 300px;
background-color: palevioletred;
}
.content{
width: 100%;
height: 300px;
background-color: yellowgreen;
}
}
}
</style>
子组件
<template>
<div>
左侧菜单区域 <br>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const list = reactive<number[]>([8, 8, 8])
const person = {
name: '张三',
age: 30
}
//将子组件的数据暴露出去,父组件才能拿到这些数据
defineExpose({
list,
person,
})
</script>
<style lang='scss' scoped>
</style>
父组件如果没传值给子组件,子组件设置默认值
子组件
<template>
<div>
左侧菜单区域 <br>
{{title}} <br>
{{data}}
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: '父组件没传值给我',
// 复杂数据需要一个函数返回值
data: () => [0, 0, 0]
})
</script>
<style lang='scss' scoped>
</style>
二、全局组件、局部组件、递归组件
组件使用频率非常高(table, input)等,这些组件几乎每个页面都在使用便可以封装成全局组件
在一个组件A通过import去引入别的组件B称之为局部组件
递归组件和我们写程序的递归一个道理,自己调用自己,通过一个条件来结束递归,不然会导致内存泄漏
全局组件
定义一个全局组件
<template>
<div>
<h2>我是全局组件card</h2>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
</script>
<style lang='scss' scoped>
</style>
将全局组件注册到vue实例上:main.ts
import { createApp } from 'vue'
import App from './App.vue'
// 引入一下全局组件
import Card from './components/card/index.vue'
// 在vue实例上挂载全局组件
createApp(App).component('Card', Card).mount('#app')
递归组件
src\components\Tree\index.vue
应用场景,分组菜单
Menu.vue
<template>
<div>
左侧菜单区域 <br>
<Tree :data="data"></Tree>
</div>
</template>
<script setup lang='ts'>
import Tree from '../../components/Tree/index.vue'
import { ref, reactive } from 'vue'
import { title } from 'process';
type TreeList = {
title: string,
icon?: string,
children?:TreeList[]
}
const data = reactive<TreeList[]>([
{
title: 'm-1',
children:[
{
title: 'm-1-1',
children:[
{
title: 'm-1-1-1'
}
]
}
]
},
{
title: 'm-2',
children:[
{
title: 'm-2-1',
children:[
{
title: 'm-2-1-1',
children: [
{
title: 'm-2-1-1-1'
}
]
}
]
}
]
},
{
title: 'm-3'
}
])
</script>
<style lang='scss' scoped>
</style>
Tree.vue
<template>
<div>
<ul v-for="(item,index) in data" :key="index">
<li>{{item.title}}</li>
<!-- 递归组件,记得结束判断 v-if -->
<Tree v-if="item?.children?.length" :data="item.children"></Tree>
</ul>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
// 引入它自身
import Tree from './index.vue'
type TreeList = {
title: string,
icon?: string,
children?:TreeList[]
}
type Props = {
data?: TreeList[]
}
defineProps<Props>()
</script>
<style lang='scss' scoped>
</style>
三、动态组件
多个组件使用同一个挂载点,并动态切换,这就是动态组件
<!-- 通过更改:is属性达到切换组件的目的-->
<component :is="A"></component>
在Content下建A.vue B.vue C.vue
index.vue
<template>
<div>
右侧内容区域
<div class="tab">
<!-- 点击切换到相应的组件上 -->
<div @click="switchCom(item)" :key="item.name" v-for="item in data">{{item.name}}</div>
</div>
<component :is="current.comName"></component>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive, markRaw } from 'vue'
import A from './A.vue'
import B from './B.vue'
import C from './C.vue'
type Tabs = {
name: string,
comName: any
}
// Pick 从Tabs中将'comName'抽取出来组成一个新类型如下:
// type Com = { comName:any }
type Com = Pick<Tabs, 'comName'>
const data = reactive<Tabs[]>([
{
name: '我是A组件',
// 使用markRaw表示组件A不需要响应式
comName: markRaw(A)
},
{
name: '我是B组件',
comName: markRaw(B)
},
{
name: '我是C组件',
comName: markRaw(C)
},
])
let current = reactive<Com>({
// 默认显示的A组件
comName:data[0].comName
})
const switchCom = (item:Tabs) =>{
current.comName = item.comName
}
</script>
<style lang='scss' scoped>
</style>