vue中常用的组件通信方式

Vue3组件常用通信方式

1.props

可以实现父子组件,子父组件,兄弟组件通信。
defineProps方法可以接收父组件传递的数据,具体使用如下:
父组件:


<template>
  <div>
    <Child info = "enen" :money = "money"></Child>
 </div>
</template>

<script setup lang = 'ts'>
    import Child from './Child.vue';
    import { ref } from 'vue';
    let money = ref(1000)
</script>

子组件:

<template>
    <div>
        <h1>我是子组件</h1>
        <p>{{ props.info }}</p>
        <p>{{ props.money }}</p>
        <!-- 可以省略props -->
        <p>{{ info }}</p>
        <p>{{ money }}</p>
    </div>
</template>

<script setup lang = "ts">
//  需要使用到defineProps方法接收父组件传递过来的数据
// defineProps是vue3提供的方法  无需引入
//此处对象|数组写法都可以
let props = defineProps({info:String,money:Number})
// let props = defineProps(['info','money'])
console.log(props)
</script>

2.自定义事件

可以实现子父组件通信。
利用defineEmits方法返回函数触发自定义事件。
父组件:

<template>
  <div>
    <Event2 @handler2 = "handler2"></Event2>
 </div>
</template>

<script setup lang = 'ts'>
    import Event2 from './Event2.vue';
    const handler2 = (param1,param2)=>{
        console.log(param1,param2)
    }
</script>

子组件:

<template>
  <div>
    <button @click="handler">点击按钮触发事件handler</button>
 </div>
</template>

<script setup lang = 'ts'>
//  利用defineEmits方法返回函数触发自定义事件 无需引入直接使用
let $emit = defineEmits(['handler2'])

const handler = ()=>{
    // 第一个参数为事件类型,剩余参数为给父组件传递的数据
    $emit('handler2','传给父组件的参数一','传给父组件的参数二')
}
</script>

3.全局事件总线

mitt方法使用:
(1)安装
在这里插入图片描述
(2)引入使用
在这里插入图片描述
子组件1:通过emit触发目标组件上的事件,并且传递数据

<template>
   <div>
      <h1>子组件1</h1>
      <button @click="handler">向子组件2传递数据</button>

   </div>
</template>
   
<script setup lang = 'ts'>
import $bus from '../../bus';

const handler = (() => {
   $bus.emit('car', '传递给Child2的数据')
})

</script>

子组件2:通过$bus.on方法绑定事件,接收数据

<template>
   <div>
      <h1>子组件2</h1>
   </div>
</template>
   
<script setup lang = 'ts'>
import { onMounted } from 'vue';
import $bus from '../../bus';
console.log($bus)

onMounted(() => {
   // 第一个参数为事件类型,第二个参数为回调函数
   $bus.on('car', (car) => {
      console.log(car)
   })
})
</script>

4.v-model

v-model 在vue3中可以实现父子组件间的数据通信。
父组件:

<template>
    <div>
        <h1>v-model info:{{ info }} {{ a }} {{ b }}</h1>
        <!-- <input type="text" v-model="info"> -->
        <!-- <Child :modelValue="info" @update:modelValue="handler"></Child> -->

        <!-- 
            v-model的使用:
            1.相当于给子组件传递props[modelValue] = 10000
            2.相当于给子组件绑定自定义事件update:modelValue
         -->
        <Child v-model="info"></Child>
        <!-- 同一组件上可以绑定多个v-model -->
        <Child1 v-model:a="a" v-model:b="b"></Child1>
    </div>
</template>

<script setup lang = 'ts'>

// v-model 收集表单数据,实现数据双向绑定
// v-model 也可以实现组件之间的通信,实现父子组件数据同步的业务
// 父组件向子组件传递数据  Props
// 子组件向父组件传递数据  自定义事件

import { ref } from 'vue'
import Child from './Child.vue';

import Child1 from './Child1.vue'
let a = ref(1)
let b = ref(3)

let info = ref(100000)
//  自定义事件接收子组件向父组件传递的数据
const handler = (n) => {
    console.log('子组件向父组件传递过来的数据是:', n)
    console.log(info, 'info')
    info.value = n
}

</script>

子组件:

<template>
    <div>
        <h1>money{{ modelValue }}</h1>
        <button @click="handler">父子组件数据同步</button>
    </div>
</template>

<script setup lang = 'ts'>
//  接收父组件数据
let props = defineProps(['modelValue'])
let $emit = defineEmits(['update:modelValue'])
const handler = () => {
    $emit('update:modelValue', props.modelValue + 10000)
}
</script>

子组件1:

<template>
    <div>
        <button @click="handler">a{{ a }}</button>
        <button>b{{ b }}</button>
    </div>
</template>

<script setup lang = 'ts'>
let props = defineProps(['a', 'b'])
let $emit = defineEmits(['update:a', 'update:b'])

const handler = () => {
    $emit('update:a', props.a + 1)
}
</script>

注意:
在使用v-model实现父子组件通信时,子组件默认的prop为 modelValue, 对应的事件为update:modelValue,若想要自行命名,则可参考官方文档,如下图所示。
在这里插入图片描述

5.useAttrs

vue3中可以获取组件上的属性和时间的方法,也可用于组件通信。
父组件:

<template>
    <div>
        <h1>Attrs-listener</h1>
         <el-button type="primary" size="small" :icon="Edit"></el-button>
        <HintButton type="primary" size="small" :icon="Edit" title="title" @click="handler" @getNum="handler"></HintButton>
    </div>
</template>

<script setup lang = 'ts'>
// vue3框架提供的一个useAttrs方法,可以获取组件上的属性和方法
import {
    Check,
    Delete,
    Edit,
    Message,
    Search,
    Star,
} from '@element-plus/icons-vue'
import HintButton from './HintButton.vue';
const handler = () => {
    console.log(123345)
}
</script>

子组件:

<template>
    <div :title="$attrs.title">
        <el-button :="$attrs"></el-button>
    </div>
</template>

<script setup lang = 'ts'>

// 引入useAttrs方法 获取组件上面的属性与方法
import { useAttrs } from 'vue';

let $attrs = useAttrs()
console.log($attrs, '组件hintButton1')

// defineProps和useAttrs都可以获取到组件上的属性和事件
//但是用defineProps接收后 useAttrs就获取不到对应的属性或者方法了
// 例如执行 defineProps(['title']) 之后,使用useAttrs无法再获取到title
</script>

在这里插入图片描述

6.ref 和 parent

ref可以获取子组件实例。
父组件代码:

<template>
    <div>
        <h1>父组件:{{ money }}</h1>
        <button @click="handler">
            跟儿子借十块钱
        </button>

        <Son ref="son"></Son>
        <Dau></Dau>

    </div>
</template>

<script setup lang = 'ts'>
import Son from './Son.vue'
import Dau from './Daughter.vue'
import { ref } from 'vue'

let money = ref(100000000)
// ref:可以获取真实的DOM节点,可以获取到子组件实例VC
// $parent:可以在子组件内部获取到父组件的实例

// 获取子组件实例
let son = ref()
const handler = () => {
    money.value += 10
    son.value.money -= 10
    // console.log(son.value);
    son.value.fly()

}
// 使用defineExpose方法将money暴露给Dau
defineExpose({
    money
})
</script>

子组件Son中需要使用defineExpose方法将数据暴露出去以供父组件获取:

<template>
    <div>
        儿子组件{{ money }}
    </div>
</template>

<script setup lang = 'ts'>
import { ref } from 'vue'
let money = ref(22222222)
const fly = () => {
    console.log('fly')
}
defineExpose({
    money,
    fly
})
</script>

子组件Dau通过parent获取父组件实例:

<template>
    <div>
        <h2>女儿组件{{ money }}</h2>
        <button @click="handler($parent)">跟父组件借10000</button>
    </div>
</template>

<script setup lang = 'ts'>
import { ref } from 'vue'
let money = ref(20000000)
const handler = ($parent) => {
    money.value += 10000
    $parent.money -= 10000
    console.log($parent, '父组件')

}
</script>

7.provide 与 inject

provide 与 inject 方法可以实现祖先组件与后代组件之间的数据传递,即在祖先组件中 provide 方法提供数据,后代组件中使用 inject 方法注入数据。
祖先组件代码:

<template>
    <div>
        <h1>Provide-inject</h1>
        <div>给后代组件{{ apple }}</div>
        <Child></Child>
    </div>
</template>

<script setup lang = 'ts'>
import Child from './Child.vue';
import { ref, provide } from 'vue'
let apple = ref('苹果')
// provide需要上传两个参数
// 第一个参数是提供数据的Key值
// 第二个参数是需要提供的数据
provide('Key', apple)

</script>

后代组件:

<template>
    <div>孙子组件接收到了{{ apple }}</div>
    <button @click="updateApple">更新数据</button>
</template>

<script setup lang = 'ts'>
import { inject } from 'vue';

// 注入祖先组件提供数据
// 需要传参数:即为祖先提供数据的Key
let apple = inject('Key')
console.log(apple)

const updateApple = () => {
    apple.value = '香蕉'
}
</script>

8.pinia

pinia:集中式管理状态容器,可以实现任意组件之间的通信。
pinia需要安装后使用,在项目根目录下执行 npm install pinia。

在这里插入图片描述
创建store文件夹 ,创建文件index引入pinia
在这里插入图片描述
在main.ts 文件中全局使用
在这里插入图片描述
info.ts小仓库代码

// 定义info小仓库
import { defineStore } from 'pinia'

// defineStore接收两个参数
// 第一个:小仓库名称  第二个:小仓库配置对象
// defineStore 方法执行会返回一个函数,函数作用是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
    state: () => {
        return {
            count: 10000,
            arr: [1, 2, 3, 4, 5, 6, 7]
        }
    },
    actions: {
        // 注意:与vuex相比函数没有context上下文对象
        // 没有commit,没有mutations修改数据
        updateNum(a: number, b: number) {
            this.count += b
        }
    },
    getters: {
        total() {
            let result: any = this.arr.reduce((prev: number, next: number) => {
                return prev + next
            }, 0)
            return result
        }
    }
})
export default useInfoStore;

在子组件一中使用info 中的数据

<template>
    <div>
        子组件Child
        <h4>{{ infoStore.count }} ---{{ infoStore.total }}</h4>
        <button @click="updateInfo">更新数据</button>
    </div>
</template>

<script setup lang = 'ts'>
import useInfoStore from '../../store/modules/info';

// 获取小仓库对象
let infoStore = useInfoStore()

const updateInfo = () => {
    // infoStore.count++
    // infoStore.$patch({
    //     count: 888888
    // })
    // 可以调用仓库的方法修改仓库的数据
    infoStore.updateNum(66, 88)
    console.log(infoStore)
}
</script>

在子组件二中使用info中的数据:

<template>
    <div>
        子组件Child1:{{ infoStore.count }}--{{ todoStore.arr }} ----{{ todoStore.total }}
    </div>
    <div @click="updateTodo">{{ todoStore.todos }}</div>
</template>

<script setup lang = 'ts'>
import useInfoStore from '../../store/modules/info';
import useTodoStore from '../../store/modules/todo';
// 获取小仓库对象
let infoStore = useInfoStore()

let todoStore = useTodoStore()

const updateTodo = () => {
    todoStore.updateData()
}
</script>

也可以使用组合式API来实现仓库创建。

// 组合式API
import { defineStore } from "pinia";
import { ref, computed } from "vue";
let useTodoStore = defineStore('tode', () => {
    let todos = ref([{ id: 1, title: '上班' }, { id: 2, title: '摸鱼' }, { id: 1, title: '打游戏' }])
    let arr = ref([1, 2, 3, 4, 5, 6])

    const total = computed(() => {
        return arr.value.reduce((prev, next) => {
            return prev + next
        }, 0)
    })
    return {
        todos,
        total,
        arr,
        updateData() {
            todos.value.push({ id: 4, title: '写bug' })
        }
    }
})

export default useTodoStore;

以上及实现组件间的数据通信。

9.slot

默认插槽,具名插槽,作用域插槽。
父组件:

<template>
    <h1>Slot</h1>   
	<!-- 作用域插槽 -->
    <Test1 :todos="todos">
        <template v-slot="{ $row, $index }">
            <h1 :style="{ color: $row.done ? 'red' : 'green' }">{{ $row.title }} --- {{ $index }}</h1>
        </template>
    </Test1>
    <!-- 默认插槽,具名插槽 -->
    <Test>
        <div>
            这里有个插槽
        </div>
        <template #a>
            <div>插槽a</div>
        </template>
        <!-- 具名插槽  v-sot指令 可以简写成# -->
        <!-- <template v-slot:b> -->
        <template #b>
            <div>插槽b</div>
        </template>

    </Test>
</template>

<script setup lang = 'ts'>
import Test from './Test.vue'
import Test1 from './Test1.vue'
import { ref } from 'vue'
// 插槽: 默认插槽 具名插槽 作用域插槽
// 作用域插槽:可以传递数据的插槽,子组件可以将数据传递给父组件,
// 父组件决定这些数据是以何种的结构或者外观展示

let todos = ref([{ id: 1, title: '上班', done: true }, { id: 1, title: '摸鱼', done: false }, { id: 1, title: '打游戏', done: true }])

</script>

Test组件:

<template>
    <h2>子组件默认插槽</h2>
    <slot></slot>
    <h2>子组件默认插槽</h2>

    <h2>子组件具名插槽a</h2>
    <slot name="a"></slot>
    <h2>子组件具名插槽a</h2>

    <h2>子组件具名插槽b</h2>
    <slot name="b"></slot>
    <h2>子组件具名插槽b</h2>
</template>

Test1组件:

<template>
    <div>
        <h1>作用域插槽</h1>
        <ul>
            <li v-for="(item, index) in todos" :key="item.id">
                <!-- 作用域插槽:可以将数据回传给父组件 -->
                <slot :$row="item" :$index="index"></slot>

            </li>
        </ul>
    </div>
</template>

<script setup lang = 'ts'>
defineProps(['todos'])
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一行代码三个错。

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

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

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

打赏作者

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

抵扣说明:

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

余额充值