四、Vue3基础四


一、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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永恒的宁静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值