前言
我们日常项目开发中,少不了组件之间的通信,我们可能只知道一些常见的方式比如props,emits,其实,实现组件间的通信有很多种方式。
正文
一、props
props用于父组件
给子组件
传参,主要步骤是:
- 父组件在引入的子组件上v-bind 绑定一个变量,值为要传递的参数。
- 子组件用defineProps来接收传过来的变量。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<div>
<input type="text" v-model="value">
<button>添加</button>
</div>
<Child :val="value"/>
</template>
<script setup>
import Child from './child.vue'
import {ref} from 'vue'
const value=ref('hello')
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<h2>{{ val }}</h2>
</template>
<script setup>
const props=defineProps({
val:String
})
</script>
<style lang="scss" scoped></style>
效果图如下:
二、emits
emits用于子组件
给父组件
传参,主要步骤是:
- 子组件通过defineEmits 注册一个事件,并在点击事件 中将这个事件携带 要传递的参数发散出去。
- 父组件在引入的子组件上绑定 这个事件,这个事件的参数就是传递过来的值。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<h2>{{ res }}</h2>
<Child @add="handleAdd" />
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const res = ref('hello')
const handleAdd = (e) => {
res.value = e
}
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<input type="text" v-model="msg">
<button @click="handle">添加</button>
</template>
<script setup>
import {ref} from 'vue'
const msg=ref('hello')
const emit=defineEmits(['add'])//注册事件
const handle=()=>{
emit('add',msg.value)
}
</script>
<style lang="scss" scoped></style>
效果图如下:
三、v-model
v-model用于子组件
给父组件
传参,主要步骤是:
- 子组件通过defineEmits 注册一个属性,并在点击事件 中将这个属性携带 要传递的参数发散出去。
- 父组件在引入的子组件上用v-model绑定这个属性,值为传递过来的值。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<h2>{{ title }}</h2>
<Child v-model:msg="title"/>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const title = ref('hello')
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<input type="text" v-model="val">
<button @click="handle">添加</button>
</template>
<script setup>
import {ref} from 'vue'
const val=ref('hello')
const emits=defineEmits(['update:msg'])
const handle=()=>{
emits('update:msg',val.value)
}
</script>
<style lang="scss" scoped></style>
效果图如下:
v-model能达到和emits一样的效果,主要区别就是父组件可以偷点懒,不用多去绑定一个点击事件了。
四、refs
refs用于子组件
给父组件
传参,主要步骤是:
- 子组件通过defineExpose 将要传递的值暴露出去。
- 父组件在引入的子组件上用ref 获取子组件的dom结构,dom结构有值 的时候读取传递的值。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<h2>{{ childRef?.val }}</h2> <!--childRef有值的时候读val-->
<Child ref="childRef"/>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const childRef=ref(null) //子组件的dom结构
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<input type="text" v-model="val">
<button @click="handle">添加</button>
</template>
<script setup>
import {ref} from 'vue'
const val=ref('')
const handle=()=>{}
defineExpose({val})
</script>
<style lang="scss" scoped></style>
效果如下:
五、provide、inject
provide、inject用于父组件
给子孙组件
传参,主要步骤是:
- 父组件通过provide将要传递的值提供出去(provide提供的一定是额外的引用类型数据)。
- 子孙组件通过inject注入传递过来的值。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<div>
<input type="text" v-model="msg">
<button @click="handle">添加</button>
</div>
<Child />
</template>
<script setup>
import Child from './child.vue'
import { provide, ref } from 'vue'
const msg = ref('')
const list=ref(['html','css'])
provide('title', list.value)
const handle = () => {
list.value.push(msg.value)
msg.value=''
}
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<h2>{{ title }}</h2>
</template>
<script setup>
import { inject } from 'vue'
const title = inject('title')
</script>
<style lang="scss" scoped></style>
效果图如下:
六、eventBus(mitt)
eventBus(mitt)用于父子
、子父
、兄弟
传参,这里以父传子为例,主要步骤是:
- 全局引入 mitt并声明vue全局的变量$emitter
- 父组件通过在点击事件中调用emit 方法将要传递的值发布出去。
- 子组件通过on 方法的回调函数的参数得到传递过来的值。
代码示例如下:
js
<!-- 父组件Parent.vue -->
<template>
<div>
<input type="text" v-model="msg">
<button @click="handle">添加</button>
</div>
<Child />
</template>
<script setup>
import Child from './child.vue'
import { ref,getCurrentInstance } from 'vue'
const msg = ref('')
const app=getCurrentInstance()
const handle = () => {
app.appContext.config.globalProperties.$emitter.emit('foo', msg.value)
}
</script>
<style lang="scss" scoped></style>
js
<!-- 子组件Child.vue -->
<template>
<h2>{{ title }}</h2>
</template>
<script setup>
import { ref,getCurrentInstance } from 'vue'
const title = ref('hello world')
const app=getCurrentInstance()
app.appContext.config.globalProperties.$emitter.on('foo',(e)=>{
title.value=e
})
</script>
<style lang="scss" scoped></style>
效果图如下:
七、vuex
vuex我们都不陌生,用于共享数据状态 ,便捷了组件间的通信 以及代码的维护,它的基础用法如下:
- 使用yarn/npm安装vuex。
- 在src下的store中新建index.js。
js
import {createStore} from 'vuex'
const store=createStore({
state(){ //仓库中的数据源
return {
count:2
}
},
mutations:{ //所有修改数据源的方法
add(state,n){
state.count+=n
}
},
actions:{ //触发mutations中的方法
addAction(context,num){
context.commit('add',num)
}
},
getters:{ //计算属性,只要依赖值变化就会自动重新执行
countSquare(state){
return state.count**2
}
}
})
export default store
- 在main.js中引入store并把其挂载到vue中。
那么,在组件中如何访问 store中的数据和方法呢?
我们从以下四块来说明:
-
state
-
使用this.$store.state.count
js<template> <div id="app"> {{ this.$store.state.count }} </div> </template>
-
使用mapState
js// 从 Vuex 中导入 mapState import { mapState } from 'vuex' export default { computed: { // 将 store 映射到当前组件的计算属性 ...mapState(['count']) } }
-
-
mutations
-
使用this.$store.commit()
jsexport default { methods: { handle() { // 触发 mutations 中的 add 方法 this.$store.commit('add', 1) } } }
-
使用mapMutations
jsimport { mapMutations } from 'vuex' export default { methods: { // 将 mutations 中的 add 方法映射到 methods 中 ...mapMutations(['add']) } }
-
-
actions
-
使用this.$store.dispatch()
jsexport default { methods: { handle(num) { // 使用 dispatch 来调用 actions 中的方法 this.$store.commit('addAction', num) } } }
-
使用mapActions
jsimport { mapActions } from 'vuex' export default { methods: { // 映射 actions 中的指定方法 到 methods中 ...mapActions(['addAction']) } }
-
-
getters
-
使用this.$store.getters.countSquare
js<template> <div id="app"> {{ this.$store.getters.countSquare }} </div> </template>
-
使用mapGetters
jsimport { mapGetters } from 'vuex' export default { computed: { // 映射 actions 中的指定方法 到 methods中 ...mapGetters(['countSquare']) } }
-
八、pinia
pinia
是vuex的升级版
,它有以下优点:
- pinia抛弃 了vuex中的mutations ,pinia中actions 兼容同步和异步。
- vuex中数据过多需分模块 进行管理,而pinia中每个store都是独立的,互不影响。
- pinia体积非常小 ,只有1KB左右。
它的基础用法如下:
- 使用yarn/npm安装pinia。
- 引入pinia并挂载
js
import App from './App.vue'
import {createPinia} from 'pinia'
createApp(App).use(createPinia()).mount('#app')
- 在src下的store中新建index.js。
js
import {defineStore} from 'pinia'
import axios from 'axios'
const useStore=defineStore('cart',{
state:()=>{ //放响应式数据源
return{
badge:0
}
},
getters:{
handleBadge(){
this.badge++
}
},
actions:{
async changeBadge(){
const res =await axios.post('/cartList', { //获取购物车数据
username: JSON.parse(sessionStorage.getItem('userInfo')).username
})
this.badge=res.data.length
}
},
persist:{ //开启数据缓存 持久化
enabled:true,
strategies:[ //缓存指定数据
{
path:['userInfo'],
storage:localStorage //指定localStorage存储
}
]
}
})
export default useStore
那么,在组件中如何访问 store中的数据和方法呢?
直接引入并调用就可以访问啦~
js
import useStore from '@/store/cart.js'
const cart=useCartStore()
cart.changeBadge()
结
以上就是vue中的通信方式,组件通信不成问题了!如有错误或还有其它通信方式欢迎大佬指出,一起讨论!