织梦网站漏洞,cms网站开发流程,个人如果做网站赚钱,南昌网站建设方案上一讲的 provide / inject API 主要解决了跨级组件间的通信问题#xff0c;不过它的使用场景#xff0c;主要是子组件获取上级组件的状态#xff0c;跨级组件间建立了一种主动提供与依赖注入的关系。然后有两种场景它不能很好的解决#xff1a;
父组件向子组件#xff0…上一讲的 provide / inject API 主要解决了跨级组件间的通信问题不过它的使用场景主要是子组件获取上级组件的状态跨级组件间建立了一种主动提供与依赖注入的关系。然后有两种场景它不能很好的解决
父组件向子组件支持跨级传递数据子组件向父组件支持跨级传递数据。
这种父子含跨级传递数据的通信方式Vue.js 并没有提供原生的 API 来支持而是推荐使用大型数据状态管理工具 Vuex而我们之前已经介绍过 Vuex 的场景与在独立组件或库中使用的限制。本小节则介绍一种在父子组件间通信的方法 dispatch 和 broadcast。
$on 与 $emit
如果您使用过较早的 Vue.js 1.x 版本肯定对 $dispatch 和 $broadcast 这两个内置的方法很熟悉不过它们都在 Vue.js 2.x 里废弃了。在正式介绍主角前我们先看看 $on 与 $emit 这两个 API因为它们是本节内容的基础。
$emit 会在当前组件实例上触发自定义事件并传递一些参数给监听器的回调一般来说都是在父级调用这个组件时使用 on 的方式来监听自定义事件的比如在子组件中触发事件
// child.vue部分代码省略
export default {methods: {handleEmitEvent () {this.$emit(test, Hello Vue.js);}}
}在父组件中监听由 child.vue 触发的自定义事件 test
!-- parent.vue部分代码省略--
templatechild-component testhandleEvent
/template
scriptexport default {methods: {handleEvent (text) {console.log(text); // Hello Vue.js}}}
/script这里看似是在父组件 parent.vue 中绑定的自定义事件 test 的处理句柄然而事件 test 并不是在父组件上触发的而是在子组件 child.vue 里触发的只是通过 v-on 在父组件中监听。既然是子组件自己触发的那它自己也可以监听到这就要使用 $on 来监听实例上的事件换言之组件使用 $emit 在自己实例上触发事件并用 $on 监听它。
听起来这种神sāo操作有点多此一举我们不妨先来看个示例
templatedivbutton clickhandleEmitEvent触发自定义事件/button/div
/template
scriptexport default {methods: {handleEmitEvent () {// 在当前组件上触发自定义事件 test并传值this.$emit(test, Hello Vue.js)}},mounted () {// 监听自定义事件 testthis.$on(test, (text) {window.alert(text);});}}
/script$on 监听了自己触发的自定义事件 test因为有时不确定何时会触发事件一般会在 mounted 或 created 钩子中来监听。
仅上面的示例的确是多此一举的因为大可在 handleEmitEvent 里直接写 window.alert(text)没必要绕一圈。
之所以多此一举是因为 handleEmitEvent 是当前组件内的 调用的如果这个方法不是它自己调用而是其它组件调用的那这个用法就大有可为了。
了解了 $on 和 $emit 的用法后我们再来看两个“过时的” API。
Vue.js 1.x 的 $dispatch 与 $broadcast
虽然 Vue.js 1.x 已经成为过去时但为了充分理解本节通信方法的使用场景还是有必要来了解一点它的历史。
在 Vue.js 1.x 中提供了两个方法$dispatch 和 $broadcast 前者用于向上级派发事件只要是它的父级一级或多级以上都可以在组件内通过 $on 或 events2.x 已废弃监听到后者相反是由上级向下级广播事件的。
来看一个简单的示例
!-- 注意该示例为 Vue.js 1.x 版本 --
!-- 子组件 --
templatebutton clickhandleDispatch派发事件/button
/template
script
export default {methods: {handleDispatch () {this.$dispatch(test, Hello, Vue.js);}}
}
/script!-- 父组件部分代码省略 --
templatechild-component/child-component
/template
scriptexport default {mounted () {this.$on(test, (text) {console.log(text); // Hello, Vue.js});}}
/script$broadcast 类似只不过方向相反。这两种方法一旦发出事件后任何组件都是可以接收到的就近原则而且会在第一次接收到后停止冒泡除非返回 true。
这两个方法虽然看起来很好用但是在 Vue.js 2.x 中都废弃了官方给出的解释是 因为基于组件树结构的事件流方式有时让人难以理解并且在组件结构扩展的过程中会变得越来越脆弱。 虽然在业务开发中它没有 Vuex 这样专门管理状态的插件清晰好用但对独立组件库的开发绝对是福音。因为独立组件一般层级并不会很复杂并且剥离了业务不会变的难以维护。
知道了 $dispatch 和 $broadcast 的前世今生接下来我们就在 Vue.js 2.x 中自行实现这两个方法。
自行实现 dispatch 和 broadcast 方法
自行实现的 dispatch 和 broadcast 方法不能保证跟 Vue.js 1.x 的 $dispatch 和 $broadcast 具有完全相同的体验但基本功能是一样的都是解决父子组件含跨级间的通信问题。
通过目前已知的信息我们要实现的 dispatch 和 broadcast 方法将具有以下功能
在子组件调用 dispatch 方法向上级指定的组件实例最近的上触发自定义事件并传递数据且该上级组件已预先通过 $on监听了这个事件相反在父组件调用 broadcast 方法向下级指定的组件实例最近的上触发自定义事件并传递数据且该下级组件已预先通过 $on监听了这个事件。
实现这对方法的关键点在于如何正确地向上或向下找到对应的组件实例并在它上面触发方法。在设计一个新功能features时可以先确定这个功能的 API 是什么也就是说方法名、参数、使用样例确定好 API再来写具体的代码。
因为 Vue.js 内置的方法才是以 $ 开头的比如 $nextTick、$emit 等为了避免不必要的冲突并遵循规范这里的 dispatch 和 broadcast 方法名前不加 $。并且该方法可能在很多组件中都会使用复用起见我们封装在混合mixins里。那它的使用样例可能是这样的
// 部分代码省略
import Emitter from ../mixins/emitter.jsexport default {mixins: [ Emitter ],methods: {handleDispatch () {this.dispatch(); // ①},handleBroadcast () {this.broadcast(); // ②}}
}上例中行 ① 和行 ② 的两个方法就是在导入的混合 emitter.js 中定义的这个稍后我们再讲先来分析这两个方法应该传入什么参数。一般来说为了跟 Vue.js 1.x 的方法一致第一个参数应当是自定义事件名比如 “test”第二个参数是传递的数据比如 “Hello, Vue.js”但在这里有什么问题呢只通过这两个参数我们没办法知道要在哪个组件上触发事件因为自行实现的这对方法与 Vue.js 1.x 的原生方法机理上是有区别的。上文说到实现这对方法的关键点在于准确地找到组件实例。那在寻找组件实例上我们的“惯用伎俩”就是通过遍历来匹配组件的 name 选项在独立组件库里每个组件的 name 值应当是唯一的name 主要用于递归组件在后面小节会单独介绍。
先来看下 emitter.js 的代码
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]));}});
}
export default {methods: {dispatch(componentName, eventName, params) {let parent this.$parent || this.$root;let name parent.$options.name;while (parent (!name || name ! componentName)) {parent parent.$parent;if (parent) {name parent.$options.name;}}if (parent) {parent.$emit.apply(parent, [eventName].concat(params));}},broadcast(componentName, eventName, params) {broadcast.call(this, componentName, eventName, params);}}
};因为是用作 mixins 导入所以在 methods 里定义的 dispatch 和 broadcast 方法会被混合到组件里自然就可以用 this.dispatch 和 this.broadcast 来使用。
这两个方法都接收了三个参数第一个是组件的 name 值用于向上或向下递归遍历来寻找对应的组件第二个和第三个就是上文分析的自定义事件名称和要传递的数据。
可以看到在 dispatch 里通过 while 语句不断向上遍历更新当前组件即上下文为当前调用该方法的组件的父组件实例变量 parent 即为父组件实例直到匹配到定义的 componentName 与某个上级组件的 name 选项一致时结束循环并在找到的组件实例上调用 $emit 方法来触发自定义事件 eventName。broadcast 方法与之类似只不过是向下遍历寻找。
来看一下具体的使用方法。有 A.vue 和 B.vue 两个组件其中 B 是 A 的子组件中间可能跨多级在 A 中向 B 通信
!-- A.vue --
templatebutton clickhandleClick触发事件/button
/template
scriptimport Emitter from ../mixins/emitter.js;export default {name: componentA,mixins: [ Emitter ],methods: {handleClick () {this.broadcast(componentB, on-message, Hello Vue.js);}}}
/script// B.vue
export default {name: componentB,created () {this.$on(on-message, this.showMessage);},methods: {showMessage (text) {window.alert(text);}}
}同理如果是 B 向 A 通信在 B 中调用 dispatch 方法在 A 中使用 $on 监听事件即可。
以上就是自行实现的 dispatch 和 broadcast 方法相比 Vue.js 1.x有以下不同
需要额外传入组件的 name 作为第一个参数无冒泡机制第三个参数传递的数据只能是一个较多时可以传入一个对象而 Vue.js 1.x 可以传入多个参数当然你对 emitter.js 稍作修改也能支持传入多个参数只是一般场景传入一个对象足以。
结语
Vue.js 的组件通信到此还没完全结束如果你想“趁热打铁”一口气看完可以先阅读第 6 节组件的通信 3。亦或按顺序看下一节的实战来进一步加深理解 provide / inject 和 dispatch / broadcast 这两对通信方法的使用场景。
注本节部分代码参考 iView。