element-ui源码之组件通信那些事
最近在用element-ui重构前端项目,无意之中翻阅到一个比较好用的组件间通信方式,借助于vue的封装的发布-订阅消息模式与mixin语法。在开始之前先总结下vue常用的组件间通信方式,具体如下:
1、props与自定义事件
优点:常用的父子、子父组件传递方式,简单易懂
缺点:子父、父子之间传参比较高效,但是爷孙,兄弟组件之间存在通信短板,只能一级级传递
2、vue 2.4中新增的$attrs与$listeners
优点:解决了组件嵌套层次较深问题,通过在组件中绑定组件的属性值与监听组件的事件监听对象,子组件可以轻松访问到上层作用域的事件回调方法
缺点:相对于props与自定义事件,只是简化了写法,本质上属性也是一级级传递的,如果组件的层级是A>B>C,C组件如果想要访问到A组件的事件回调,那么在B组件中需要绑定A组件的属性attrs与事件对象listeners。
3、借助于发布-订阅模式
优点:发布-订阅设计模式所擅长点就是解决模块间通信,无论何种方式的通信方式,理论上都可以解决,在vue中,大量使用了该设计模式,只需要实例化一个vue实例,作为一个单独的事件中心引入即可。发布订阅模式具体内容可参考:https://www.cnblogs.com/gerry2019/p/10241488.html
缺点:发布-订阅模式只要有订阅消息,在分发事件的时候,都会触发订阅的回调,所以在开发过程中要注意自定义事件的命名空间,防止一些不必要的触发操作。
4、借助vuex管理状态
优点:引入状态管理,可以统一的管理项目状态,针对大型复杂项目适用性较好
缺点:需要额外维护一个vuex对象,中小型项目不适合
5、vue 2.2中新增的provide/inject
优点:可以从父组件注入需要共享给子元素的数据,如果注入的的数据是响应式,那么组件之间可以实现双向数据通信
缺点:官方并不推荐在业务代码中使用此模式,因为这种双向通信可能会导致组件的状态发生混乱
以上为vue中常用的几种组件间通信方式,以下为element-ui中采用的组件通信方式,本质上也是发布-订阅模式,与上述方法3大致相似,只是引入了自定义事件的生效的作用域,可以规避一些意料之外的事件触发。因为稍微不注意消息的命名空间,就可能会导致一些意外之外的错误,除此之外,当两个模块功能相似,如果单独都写一遍,可能存在大量冗余代码,在vue中我们想到的策略可能是借助于mixin,抽离公共的业务代码,但是假如抽离出了公共的消息订阅内容,那么此时问题就来了,如果我只想A模块内容发生改变,B模块不变,这个就会存在策略性问题,element-ui中的通信策略可以很好的规避掉以上两种坑,具体实现逻辑如下:
/** * * @param {目标组件名称} componentName * @param {事件名称} eventName * @param {载荷参数} params */ function broadcast(componentName, eventName, params) { //递归子组件,查找命名空间内组件 this.$children.forEach(child => { var name = child.$options.componentName; if (name === componentName) { //分发子组件内订阅消息 child.$emit.apply(child, [eventName].concat(params)); } else { broadcast.apply(child, [componentName, eventName].concat([params])); } }); } export default { methods: { /** * * @param {目标组件名称} componentName * @param {事件名称} eventName * @param {载荷参数} params */ dispatch(componentName, eventName, params) { var parent = this.$parent || this.$root; var name = parent.$options.componentName; //循环查询父组件,找到目标父组件 while (parent && (!name || name !== componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.componentName; } } if (parent) { //分发父组件订阅内容 parent.$emit.apply(parent, [eventName].concat(params)); } }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); } } };
从以上代码可以看出,在组件通信过程中,需要额外加一个componetName属性,其主要作用就是对分发的事件加入一个命名空间,命名空间内部的事件才会触发,否则会直接跳过,增加了业务代码的容错能力。在使用的过程中,只需要在需要引用的地方引入这个混入,就可以实现组件间双向通信。element-ui中的具体使用如下:
mixin语法可参考:https://cn.vuejs.org/v2/guide/mixins.html
mixin本质上就类似于继承,有助于简化业务代码
vue 2.x中部分新增组件通信方式可参阅vue API文档:https://cn.vuejs.org/v2/api/#provide-inject
- Element UI table组件源码分析
- 完美解决Vue+element-ui 中upload组件使用多个时无法绑定对应的元素
- 饿了么组件库element-ui正则表达式验证表单,后端验证表单
- VUE 使用新版本 element-ui 组件库 Select 组件时, value 值为对象时的 BUG 处理
- element ui dialog 父子组件传值
- Element-ui时间联动组件优化
- 基于Vue+ElementUI的省市区选择公共组件
- taintdroid源码分析之四 组件(进程)通信间污点传播
- vue中通过修改element-ui的类修改相关组件的样式
- 用element-ui之el-form、el-table、el-pagination组件实现列表展示和查询条件的渲染
- element-ui 日期组件el-date-picker数据格式问题
- 在vue项目中使用element-ui的Upload上传组件的示例
- element-ui的组件message
- 如何在vue项目中使用递归组件配合Element-ui布局侧边栏
- vue2.0 使用element-ui里的upload组件实现图片预览效果方法
- 微信小程序:有赞小程序UI( vant-weapp ) actionsheet组件源码窥探
- 轻量级C#网络通信组件StriveEngine —— C/S通信开源demo(附源码)
- 在vue项目中使用element-ui的Upload上传组件
- element-UI 弹出外部组件
- UI 组件库之Element