Vue — 组件通信(三)

最后更新于:2022-04-02 08:08:45

>[success] # 组件通信(三) ~~~ 1.了解'$on'与'$emit' 2.根据Vue.js 1.x 的 '$dispatch' 与 '$broadcast' 两方法思路伪造一个, 属于vue.js 2.x 系列同样的方法.其中 '$dispatch'子传父或者孙子传爷爷, '$broadcast' 则相反,但是只会找到最近的触发 ~~~ >[info] ## 了解 '$on'与'$emit' ~~~ 1.使用 $on(eventName) 监听事件第二个参数是个方法, 使用 $emit(eventName) 触发事件第二个参数是传值, 这两个方法常见的使用场景就是'bus' 2.要注意 '$on' 监听需要在 mounted 或 created 钩子中来监听。 3.可以参考笔者另一个笔记内容'前端知识 -- 学习整理',这个思路就是设计 模式中的'发布订阅'模式 ~~~ >[danger] ##### 不是只有bus 才能用 ~~~ 1.$on 监听了自己触发的自定义事件 test,因为有时不确定何时会触发事件, 一般会在 mounted 或 created 钩子中来监听。 ~~~ * 自己触发自己的$emit 和 $on,下面写法多此一举建议直接handleEmitEvent触发alter ~~~ ~~~ * 运行效果 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/e0e510d7ef4262335aca864d09748cc2_649x190.png) >[info] ## 自定义 dispatch 和 broadcast 方法 思路 ~~~ 1.在vue1.x 中'$dispatch' 是子孙组件向父爷组件传递,'broadcast '则相反, 但是只会找到最近的触发 2.两者都是使用'$on' 触发 ~~~ >[danger] ##### 定义前看一下vue1.x版本使用 ~~~ 1.在这里 还要强调说明监听的'$on'要在 mounted 或 created 钩子中来监听。 ~~~ * 在子组件定义 ~~~ ~~~ * 在父组件中使用 ~~~ ~~~ >[danger] ##### 封装思路 ~~~ 1.通过上面1.x案例可以看出来,大体上需要两个参数一个是触发的事件名字,一个是传 递的参数,但是我们自定义的需要使用一个标记参数,来找到我们需要匹配的组件,如何 找这里就利用创建组件的使用name属性如下图 ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/07b1a1d66d13d37193f498972742ea8a_539x151.png) >[info] ## 正式封装 ~~~ 1.思路其实就是利用 '$on' 和 '$emit' 配合,通过我们自己写的这个方法,让对应 要调用的父组件或者子组件,this指向他本身,这样形成给其自身加入对应的方法, 但可以有子组件或父组件的传值 2.首先明确一点'$emit' 和 '$on' 是典型的发布的订阅模式,'$on'通过订阅的方式,来接受'$emit' 发布的内容,当然他的发布和订阅 必须是同一个'this',因此利用vue自带的'$parent' 和'$children' 从父组件开始找也好,从子组件开始找也好,原理就是层级向上找让彼此可以发布订阅在一起 ~~~ 代码参考[iView](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fiview%2Fiview%2Fblob%2F2.0%2Fsrc%2Fmixins%2Femitter.js)。 >[danger] ##### 从子组件传递给父组件甚至爷爷组件 ~~~ 1.获取当前组件的'parent',因为刚才构想过,我们需要传递三个参数,其中一个参数要匹配 的目标组件名字,因此逻辑就是去找'parent' 和要匹配的名字是否一致,找到符合的'parent' 给这个父组件添加'$emit'事件,在这个父组件中使用'$on' 去触发 ~~~ ~~~ // 子传父,孙子传爷爷或者爸爸 /* * @params:componentName 对应组件名 ,eventName 事件名称,params 函数参数 * */ dispatch(componentName, eventName, params) { let parent = this.$parent || this.$root; let name = parent.$options.name; console.log(this.$options) console.log(this.$options.name) // 递归一层一层找 while (parent && (!name || name !== componentName)) { parent = parent.$parent; // 有父组件名称 在去找组件name,如果没有了 就不用特意去找 if (parent) { name = parent.$options.name; } } if (parent) { parent.$emit(parent, [eventName].concat(params)); // parent.$emit(eventName,params); } }, ~~~ >[danger] ##### 从爷爷组件给孙子组件,父传子 ~~~ 1.思路就找每一个children,找到是否 有和名字匹配的并且绑上$emit()事件 ~~~ ~~~ function broadcast(componentName, eventName, params) { this.$children.forEach(child => { const name = child.$options.name; if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { broadcast.apply(child, [componentName, eventName].concat([params])); } }); } ~~~ >[danger] ##### 将所有代码放到mixins进行存储使用(最后的封装) ~~~ function broadcast(componentName, eventName, params) { // 当前的this 指向是在methods broadcast 中被改变了 // 因此现在的this 是每一个组件的this this.$children.forEach(child => { const name = child.$options.name; if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { // 递归 更改新的this,获取新的组件中的children,也就是更新成当前父组件的this broadcast.apply(child, [componentName, eventName].concat([params])); } }); } export default { methods: { // 子传父,孙子传爷爷或者爸爸 /* * @params:componentName 对应组件名 ,eventName 事件名称,params 函数参数 * */ dispatch(componentName, eventName, params) { let parent = this.$parent || this.$root; let name = parent.$options.name; console.log(this.$options) console.log(this.$options.name) while (parent && (!name || name !== componentName)) { parent = parent.$parent; // 有父组件名称 在去找组件name,如果没有了 就不用特意去找 if (parent) { name = parent.$options.name; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); // parent.$emit(eventName,params); } }, broadcast(componentName, eventName, params) { // 当前this 也就是其中的父组件 有了broadcast方法 broadcast.call(this, componentName, eventName, params); } } }; ~~~ >[danger] ##### 使用 ~~~ 1.要注意'$on' 要放到mounted 或 created 去使用 ~~~ * 父组件 ~~~ ~~~ * 子组件 ~~~ ~~~
';