Vue3 组件通信全解析:14 种方式助力前端开发

笔记哥 / 05-09 / 28点赞 / 0评论 / 265阅读
对于日常使用`vue3`开发项目的前端小伙伴来说,`组件通信方式`可以说是必会的基本功,今天带大家一起盘下`vue3`的通信方式。 我们这里按照`组件的关系`来划分。总共包含`14中组件通信方式`。 - 一、父子通信 1. `props` 2. `defineEmits` 3. `$attrs` 4. `$ref + defineExpose` 5. `$parent` 6. `作用域插槽` 7. `v-model` - 二、兄弟组件通信 1. `mitt` 2. `$parent` 3. `vuex/pinia` 4. `app.config.globalProperties` - 三、跨层级通信 1. `mitt` 2. `vuex/pinia` 3. `provide/inject` - 四:其它方式 - 浏览器本地存储`storage` - 全局`window`对象 - ES6模块化import/export ## 一:父子通信 ### 1.1、父传子:props 最最常用的通信方式是`props`了,父组件通过`props`方式将属性传递给子组件,子组件接受`props`并用于数据操作和页面渲染。 > 注意:子组件不要直接修改父组件传递过来的props,保持自上而下单项数据流,这样会让数据的流向十分清晰,方便后续维护! ```text // Parent.vue // Child.vue ``` ### 1.2、子传父:`defineEmits` 通过`defineEmits`可以让子组件的值传递到父组件中。 其用法如下: 1. 先在子组件中用`defineEmits([...emitName])`定义一个或多个`emit`,它的返回值是一个`emits`函数,然后可以通过调用`emits`函数向父组件发射时间,并携带参数。 ```text // Child.vue ``` 1. 在父组件中通过`@符 + 事件名`监听子组件发射出来的事件,并接收其传过来的值。 ```text // Parent.vue ``` 在`vue2`的组件中还可以通过`this.$on`、`this.$emit`来`监听`、`发射`事件,以达到传值的目的,但在`vue3`中`已废弃`这种写法。 ### 1.3、`$attrs` 如果需要在子组件中接收的`props`很多,如果在`props`声明比较繁琐,所以vue给我们提供了一个优雅的解决方案,即`$attrs`,`$attrs`指的是父组件传递给子组件的所有属性中,剔除在`props`中定义的那部分之后,剩下的就会放在`$attrs`中。 举个例子: ```text // Parent.vue ``` 这里父组件给子组件传递了两个属性`msg1`、`msg2`。 ```text // Child.vue ``` 这里子组件使用了`defineProps`定义了`msg1`,则页面中`$attrs`的值为`{ msg2: 2 }`。 还可以使用`v-bind`将`$attrs`的所有数据,以属性的方式全部传递到子组件中,我们平常在封装组件的时候,这个东西就能帮助我们实现组件的属性透传,十分好用。 ```text ``` > 注意:在vue3中`$listeners`已废弃,无法使用。 ### 1.4、$ref + defineExpose 通过`$ref`可以拿到组件的实例,`defineExpose`可以显式指定在 ` ``` 1. 在父组件中通过`ref`拿到组件实例并调用子组件暴露的`update`方法。 ```text // Parent.vue ``` ### 1.5、`$parent` `$parent`代表`当前组件的父组件实例`,如果当前组件是顶层组件,则`$parent`的值为`null`。 我们可以通过`$parent`拿到父组件的实例,自然就可以进行父子组件的交互了。一般也是和`defineExpose`配合使用,和`$ref + defineExpose`用法类似,这里就不多说了。 > 注意:`$children`在vue3中已经废弃,无法使用。 ### 1.6、作用域插槽 通过`作用域插槽`可以实现子组件向父组件传递数据。 子组件代码: ```text ``` 子组件可以在`slot`标签上传递数据给父组件。 父组件代码: ```text ``` 父组件用`v-slot`来接收数据,并渲染到页面上。 ### 1.7、v-model `v-model`可以在组件上使用以实现双向绑定,`vue`内部会帮你传递值和绑定事件,也是达到了父子组件通讯的效果。 从`vue3.4`开始,还可以使用`defineModel`便利宏,其用法如下: 子组件代码: ```text // Child.vue ``` 父组件代码: ```text // Parent.vue ``` `defineModel`的返回值就是一个`ref`,你可以随意访问和修改它,并且它会和父组件的`v-model`绑定的值保持同步,也就是实现了`双向绑定`。 ## 二、兄弟组件 两个兄弟关系组件进行通信,我们一般会借助`第三方媒介`。 ### 2.1、mitt `mitt`相当于我们`vue2`的事件总线`$bus`,只是`vue3`将其废弃,所以我们借助`mitt`实现类似`$bus`的效果。 用法如下: 1. 安装`mitt` ```text npm install mitt ``` 1. 初始化`mitt` ```text // emitter.js import mitt from'mitt'; export default mitt(); ``` 兄弟组件1: ```text ``` 兄弟组件2: ```text ``` ### 2.2、$parent 我们可以把`状态(即数据)`定义在父组件中,两个兄弟组件可以借助其`共同的父组件`共享同一份数据,间接实现通信。 ### 2.3、vuex/pinia `vuex`是`vue官方`提供的`状态管理工具`,用它可以实现`全局的状态共享`,自然也可以实现兄弟组件的通信了。当然也可以使用`pinia`替代`vuex`。 ### 2.4、app.config.globalProperties `app.config.globalProperties`是一个全局的对象,在应用内所有组件实例都能访问到,当组件属性名和它发生同名冲突时,采取`就近原则`,以组件的为准,这个就相当于`vue2`的`Vue.prototype`。 ## 三、跨层级通信 ### 3.1、mitt `mitt`可以实现全局的通信,这个在上面介绍`兄弟组件通信`的时候已经说过,这里不再多说了。 ### 3.2、vuex/pinia `vuex`和`pinia`都是全局的状态管理工具,跨层级通信也不再话下。 ### 3.3、provide/inject `provide/inject`是`vue3`提供的可以跨层级通信的方式。 其用法如下: 1. 在`父组件/根组件`中定义`provide`,提供数据。 ```text // App.vue ``` 1. 在`子组件/孙子组件`中使用`inject`,注入数据。 ```text // 后代组件 ``` ## 四、其它方式 ### 4.1、浏览器本地存储`storage` `html5`提供了一套`storage API`,包括了`localStorage`和`sessionStorage`,它实现`持久化存储、缓存`等功能,自然也可以用来组件间通信了。 ```text // 组件A // 组件B ``` 这里我在组件A使用`sessionStarge`设置了一个`name`值,在组件B里面就能拿到了,只要保证`获取值在设置值之后执行`就行了。 ### 4.2、全局`window`对象(不推荐使用) `window`作为一个全局对象,当然也可以使用它来通信了,不过它既然谁都可以访问到,就存在如下问题: 1. `命令冲突问题`; 2. `难以追踪数据修改,可维护性差`; 3. `挂在window上的数据难以销毁,从而造成内存泄漏`。 所以不推荐使用`window`对象进行通信。 ### 4.3、 ES6模块化import/export 我们可以使用ES5的模块化规范`import/export`实现通信。 ```text // a.js export let a = undefined; setTimeout(() => { a = 1; }, 1000) // b.js import { a } from './a.js' setTimeout(() => { console.log(a); // 打印1 }, 2000) ``` 由于`ES module`采用的是`符号绑定`,所以就算`export`的值是一个`基本数据类型的值`,后续修改了也能访问到。 ## 小结 上面介绍了总结了`14种vue3的组件通信方式`,包括: 1. `props` 2. `defineEmits` 3. `$attrs` 4. `$ref + defineExpose` 5. `$parent` 6. `作用域插槽` 7. `v-model` 8. `mitt` 9. `vuex/pinia` 10. `app.config.globalProperties` 11. `provide/inject` 12. 浏览器本地存储`storage` 13. 全局`window`对象 14. ES6模块化`import/export` 希望能对大家有帮助!