Vue3:通信组件

1.Props

父传子:直接传递需要获取的属性

子传父:需要借助函数,也就是方法,通过传递函数,子接着入参给函数,父调用函数即可获取到参数。

父:

<template>
    <div class="father">
        <h3>父组件</h3>
        <h4>汽车:{{ car }}</h4>
        <h4 v-show="toy">儿子给的玩具:{{ toy }}</h4>
        <Child :car="car" :sendToy="getToy" />
    </div>
</template>


<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from 'vue';

let car = ref('奔驰')
let toy = ref('')

function getToy(value: string) {
    console.log(value)
    toy.value = value
}
</script>

子:

<template>
    <div class="child">
        <h3>子组件</h3>
        <h4>玩具:{{ toy }}</h4>
        <h4>父亲给的汽车:{{ car }}</h4>
        <button @click="sendToy(toy)">给父亲玩具</button>
    </div>
</template>
    
    
<script setup lang="ts" name="Child">

import { ref } from 'vue';

let toy = ref('奥特曼')

defineProps(['car', 'sendToy'])
</script>

2.自定义事件

使用@,声明一个自定义事件,并绑定一个函数,传递给子子组件通过接收该自定义事件,

     <!-- 自定义事件 -->
        <Child  @send-toy="saveToy" />

 子接收自定义时间,并绑定点击事件,通过入参,实现数据的传递

let emit = defineEmits(['send-toy'])
    <button @click="emit('send-toy',toy)">传递玩具给父亲</button>

3.mitt(绑定-订阅接收)任意组件消息传递(类似于RabbitMQ)

import mitt from 'mitt'
import { ref } from 'vue'

let emitter = mitt()

let num = ref(0)

// 绑定事件
emitter.on('add', () => {
    console.log('add被调用了', num)
})
// 每隔1秒执行事件
setInterval(() => {
    emitter.emit('add')
}, 1000)

setTimeout(() => {
    // 解绑事件
    emitter.off('add')
}, 5000)

export default emitter

接收数据,需要绑定事件,也就是订阅。避免内存溢出,组件卸载之后,解绑事件

let food = ref('')
emitter.on('send-food', (value: string) => {
    food.value = value
})

onUnmounted(() => {
    emitter.off('send-food')
})

发送数据,需要执行事件

     <button @click="emitter.emit('send-food', food)">分享食物给弟弟</button>

4.v-model,一般用于UI库的底层实现

例如el input 输入框,底层编写了输入框组件,并且通过v-model实现数据的双向绑定

5.$attrs

父组件向子组件传递数据,attrs存储的是被defineProps接收的值,可以通过$attrs获取

<Child :car="car" :sendToy="getToy" @send-toy="saveToy" :a="a" :b="b" :updateA="updateA"/>
defineProps(['car', 'sendToy'])
   <h4>attrs:a:{{ $attrs.a }}</h4>
   <h4>attrs:b:{{ $attrs.b }}</h4>
   <button @click="$attrs.updateA(6)">修改a</button>

6.$refs,$parent

都需要集合defineExpose,将需要传递的数暴露出去

$refs :子传父

 <button @click="addBook($refs)">给孩子买书</button>
 <Child ref="c1" />


let c1 = ref()
function updateA(value: number) {
    a.value += value
}
defineExpose({book})

$parent:父传子

defineExpose({ house })
<button @click="minusHouse($parent)">得到父亲的一套房子</button>



function minusHouse(parent:any){
    console.log(parent)
    parent.house -=1
}

7.provide(提供数据)、inject(接收数据)

let money = ref(100)
provide('moneyContext',{money,updateMoney})
    <h4>父亲的钱:{{ money }}</h4>
    <button @click="updateMoney(6)">花父亲的钱</button>

let { money, updateMoney } = inject('moneyContext', { money: 55, updateMoney: (value: number) => { } })

8.插槽slot

8.1 默认插槽

引入子组件,组间标签内编写内容

在子组件,使用<slot>j进行占位

<template>
    <div class="father">
        <h2>父组件</h2>
        <div class="content">
            <Child title="今日游戏列表">
                <ul>
                    <li v-for=" (item, index) in games" :key="index">{{ item }}</li>
                </ul>
            </Child>
            <Child title="今日美食推荐">
                <img :src="imgUrl">
            </Child>
            <Child title="今日影视推荐">
                <video :src="videoUrl" controls />
            </Child>
        </div>

    </div>


</template>


<script>
export default {
    name: 'Father'
}

</script>
<script setup>
import Child from './Child.vue';
import { ref, reactive } from 'vue';

let games = reactive([
    '王者荣耀',
    '绝地求生',
    '天天酷跑'
])

let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')

let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script>


<style scoped>
.father {
    background-color: antiquewhite;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 10px;
}

.content {
    display: flex;
    justify-content: space-evenly;
}

img,
video {
    width: 90%;
}
</style>
<template>
    <div class="child">
        <h2>{{ title }}</h2>
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'Child'
}
</script>

<script setup>
defineProps(['title'])
</script>

<style scoped>
.child {
    background-color: aquamarine;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
    width: 200px;
    height: 300px;
}

h2 {
    background-color: yellow;
    text-align: center;

}
</style>

8.2 具名插槽:使用多个插槽,填充不同的内容

数据在父组件,结构也在父组件,填充的位置由子组件定义。

<template>
    <div class="father">
        <h2>父组件</h2>
        <div class="content">

            <Child>
                <template v-slot:s1>
                    <h2>今日游戏列表</h2>
                </template>
                <template v-slot:s2>
                    <ul>
                        <li v-for=" (item, index) in games" :key="index">{{ item }}</li>
                    </ul>
                </template>
            </Child>
            <Child>
                <template v-slot:s1>
                    <h2>今日美食推荐</h2>
                </template>
                <template v-slot:s2>
                    <img :src="imgUrl">
                </template>

            </Child>
            <Child>
                <template #s1>
                    <h2>今日影视推荐</h2>
                </template>
                <template #s2>
                    <video :src="videoUrl" controls />
                </template>
            </Child>
        </div>

    </div>


</template>


<script>
export default {
    name: 'Father'
}

</script>
<script setup>
import Child from './Child.vue';
import { ref, reactive } from 'vue';

let games = reactive([
    '王者荣耀',
    '绝地求生',
    '天天酷跑'
])

let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')

let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script>


<style scoped>
h2 {
    background-color: yellow;
    text-align: center;

}

.father {
    background-color: antiquewhite;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 10px;
}

.content {
    display: flex;
    justify-content: space-evenly;
}

img,
video {
    width: 90%;
}
</style>
<template>
    <div class="child">
        <slot name="s1">默认内容1</slot>
        <slot name="s2">默认内容2</slot>
    </div>
</template>

<script>
export default {
    name: 'Child'
}
</script>

<script setup>
</script>

<style scoped>
.child {
    background-color: aquamarine;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
    width: 200px;
    height: 300px;
}
</style>

8.3 作用域插槽

数据在子组件,结构在父组件,需要传递数据给父组件

<template>
    <div class="father">
        <h2>父组件</h2>
        <div class="content">

            <Child1>
                <template v-slot:s1>
                    <h2>今日游戏列表</h2>
                </template>
                <template v-slot="params">
                    <span>{{ params }}</span>
                    <ul>
                        <li v-for="item in params.youxi" :key="item.id">{{ item.name }}</li>
                    </ul>
                </template>
            </Child1>


            <Child1>
                <template v-slot:s1>
                    <h2>今日游戏列表</h2>
                </template>
                <template #default="params">
                    <span>{{ params }}</span>
                    <ol>
                        <li v-for="item in params.youxi" :key="item.id">{{ item.name }}</li>
                    </ol>
                </template>
            </Child1>

          
        </div>

    </div>


</template>


<script>
export default {
    name: 'Father'
}

</script>
<script setup>
import { ref, reactive } from 'vue';
import Child1 from './Child1.vue';

let games = reactive([
    '王者荣耀',
    '绝地求生',
    '天天酷跑'
])

let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')

let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script>


<style scoped>
h2 {
    background-color: yellow;
    text-align: center;

}

.father {
    background-color: antiquewhite;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 10px;
}

.content {
    display: flex;
    justify-content: space-evenly;
}

img,
video {
    width: 90%;
}
</style>
<template>
    <div class="child1">
        <slot name="s1"></slot>
        <slot :youxi="games"></slot>
    </div>

</template>


<script lang="ts" setup name="Child1">
import { reactive } from 'vue';

let games = reactive([
    {
        id: 111,
        name: "王者荣耀"
    },
    {
        id: 2222,
        name: "QQ飞车"
    },
    {
        id: 333,
        name: "斗地主"
    }
])
</script>


<style scoped>
.child1 {
    background-color: aquamarine;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
    width: 200px;
    height: 300px;
}
</style>

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值