vue3组件间的通讯方式

在vue3中我们可以用很多方式来实现父与子、子与父、兄弟与兄弟等不同组件的通信。接下来我会展示常用的几种通信方式以及他们各自的特点和使用细节。

props传递

props是最常见的,父组件在调用子组件时通过绑定变量向子组件传递数据。

javascript 复制代码
// parent.vue
<template>
  <Child :msg="message" :count="count" />
</template>

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

const message = ref('Hello from Parent')
const count = ref(0)
</script>
javascript 复制代码
// child.vue
<template>
  <div>
    <p>消息: {{ msg }}</p>
    <p>数量: {{ count }}</p>
  </div>
</template>

<script setup>
defineProps({
  msg: String,
  count: Number
})
</script>

特点:只能父向子传递配置、数据、状态等,不能子向父传递。

使用细节:需要在defineProps里进行变量的声明才能使用。直接解构props没有响应式,需要用toRefs()

自定义事件

自定义事件(defineEmits)的方式可以实现子向父组件传递数据,自定义事件有很多变式写法,此处只演示最简单的使用方法,便于理解中心思想。

javascript 复制代码
// parent.js
<template>
  <p>{{ message }}</p>
  <Child @updateMsg=update />
</template>

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

const message = ref('Hello from Parent')
const update = (val) => {
    message.value = val
}
</script>
javascript 复制代码
// child.js
<template>
  <div>
    <input :value @update=update></input>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const emit = defineEmits(['update-msg']) 
const msg = ref('Hello from child')
const update = (e) => {
    msg.value = e.target.value
    emit('update-msg', msg.value)
}
</script>

特点:自定义事件是通过调用defineEmits显式声明抛出一个父组件监听的事件来实现更新父组件自身的变量。

使用细节:vue3废除了 <math xmlns="http://www.w3.org/1998/Math/MathML"> o n 和 on和 </math>on和emit, 转而用defineEmits来实现自定义事件。 在使用时需要先在调用子组件时声明自定义事件名才能在子组件中进行抛出。

provide和inject

provide和inject适用于祖先组件往后代组件传递数据,可以避免层层props传递。

javascript 复制代码
// parent.js
<template>
  <p>{{ message }}</p>
  <Child @updateMsg=update />
</template>

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

const message = ref('Hello from Parent')
provide('message', message)
</script>
javascript 复制代码
// child.js
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

特点:provide/inject可以不用层层传递,在祖先组件进行一次provide声明后续的后代组件都可以通过inject进行调用。

注意事项:在vue3中provide和inject在使用前都需要进行import引入, provide上传的变量如果是用的ref声明,那么子组件更新provide,父组件inject的变量值也会更新。

vuex

vue3不建议使用vuex,但是vuex的作为很常见的设计思想还是有必要进行了解。

使用vuex需要先新建一个store文件夹,然后在文件夹下新建一个index.js文件作为store初始化文件

javascript 复制代码
// store/index.js
import {createStore } from 'vuex'

const selfStore = createStore({
    state() { //变量声明
        return {
            message: 'hello vuex'
        }
    },
    mutations: { // 实际上执行变量操作的部分,该部分是同步的
        UPDATE_MESSAGE(state, payload){
            state.message = payload
        }
    },
    actions: { //也能实现对变量的操作,本质上是通过commit来借助
    //mutations来实现对变量的操作,是异步的
        updatedMessage({commit}, payload){
            commit('UPDATE_MESSAGE', payload)
        }
    },
    getters: { //变量的getter接口,通过调用getters可以获得store内部变量
        message: state => state.message
    }
})

export default selfStore

然后在main.js文件中通过app.use()挂载store

javascript 复制代码
import { createApp } from 'vue'
import selfStore from '../src/store'
import parent from '../src/components/communication/parent.vue'
import child from '../src/components/communication/child.vue'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)
app.use(selfStore)

app
.component('parent', parent)
.component('child', child)

app.mount('#app')

在父子组件中分别进行调用store实现子组件向父组件通讯效果

javascript 复制代码
// parent.js
<template>
  <p>{{ message }}</p>
  <Child />
</template>

<script setup>
import { ref } from 'vue'
import { useStore } from 'vuex'
import Child from './Child.vue'

const store = useStore()
const message = computed(() => store.getters.message)
</script>
javascript 复制代码
// child.js
<template>
  <div>
    <input :value=msg @change=changeMsg />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useStore } from 'vuex'

const msg = ref('this is child')
const store = useStore()
const changeMsg = (e) => {
    msg.value = e.target.value
    store.dispatch('updatedMessage', e.target.value)
}
</script>

特点:vuex也是经过一次声明就可以在多个组件中进行调用,不用层层传入。在声明store时就已经声明了变量的初始化、修改、获取等所有行为,在组件使用时不用重新声明。

注意事项:在使用vuex时我们需要手动引入createStore和useStore。vuex的设计思想是单一数据源,但是也可以设置多个store,只是需要在一个js文件里进行声明。在更新数据时建议使用actions来更新数据。

在调用数据是需要用computed对store的变量进行响应式赋值,如果直接进行赋值不通过computed,那么变量不具备响应式。

pinia

pinia是vux推荐的store管理库,和vuex不同的是pinia的设计理念就是为了实现多store情况。

在创建vue3项目时会自动创建一个stores文件夹,我们所有的store文件都放入其中进行管理。

pinia的store有两种写法,一种是传统的声明state、actions、getters的选项式写法,一种是省略state、actions、getters的组合式写法,本文中演示更简单的组合式。

javascript 复制代码
// stores/counter.js
import {defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})

然后在main.js文件中通过app.use()挂载pinia

javascript 复制代码
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import parent from '../src/components/communication/parent.vue'
import child from '../src/components/communication/child.vue'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)
app.use(createPinia())

app
.component('parent', parent)
.component('child', child)

app.mount('#app')

在父子组件中分别进行调用pinia实现子组件向父组件通讯效果

javascript 复制代码
// parent.js
<template>
  <p>{{ counterStore.doubleCount }}</p>
  <Child />
</template>

<script setup>
import { ref } from 'vue'
import { useCounterStore } from '../../stores/counter'
import Child from './Child.vue'

const counterStore = useCounterStore()
</script>
javascript 复制代码
// child.js
<template>
  <div>
    <button @click="buttonClick">{{ counterStore.doubleCount }}</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useCounterStore } from '../../stores/counter'

const buttonClick = () => {
    counterStore.increment()
}
</script>

特点: pinia支持多个store,每个store单独存放在一个js文件里并通过export进行导出,在需要使用时可以直接通过import进行导入就可以进行使用。和vuex不同的是,pinia的store由pinia直接进行管理,

注意事项:在使用pinia时要注意选项式和组合式语法不能同时在一个store中进行使用。pinia自身的state就具备响应式但如果将state里的变量赋给组件中的变量,也需要进行computed修饰使其具有响应式。

相关推荐
三十_A3 小时前
【实录】使用 patch-package 修复第三方 npm 包中的 Bug
前端·npm·bug
下位子3 小时前
『AI 编程』用 Claude Code 从零到一开发全栈减脂追踪应用
前端·ai编程·claude
tyro曹仓舒3 小时前
Vue单文件组件到底需不需要写name
前端·vue.js
用户47949283569153 小时前
面试官:讲讲2FA 双因素认证原理
前端·后端·安全
乐影3 小时前
TS 模板字符串类型:从基础到进阶的类型编程魔法
前端·typescript
龙在天3 小时前
CSS 属性值的计算与过程
前端
云鹤_3 小时前
【Amis源码阅读】组件注册方法远比预想的多!
前端·低代码
xinfei3 小时前
ES6 新特性 从 ECMAScript 2015(ES6)到 ECMAScript 2025
前端
GBVFtou3 小时前
vue响应式 track 和trigger 过程
前端