vue兄弟组件之间传值
前面说了
父组件 >>>> 子组件
子组件 >>>> 父组件
父组件 <<<< 子组件
子组件 <<<< 父组件
相互传值的方式,那两个组件不是父子关系咋办啊?之前面试被问到,问的我一脸懵逼,我当时回答说,localstorage、定义全局变量;
其实这两种方式也不是不可以,但老二存了数据进去,老大怎么获取呢?老大不知道你啥时候存进去了呀!?
面试结束我就想,移动端开发有广播,注册广播,发送广播的机制,记得浏览器也有这种机制,那是不是vue里边也有相应的机制呢?
我一搜,哎呦 ~~我去~~ 真有,(妹的,之前也接触过的,就是通过bus传递,没实际在项目里边用过,所以忘了)。
那这个bus具体咋整涅:
1.定义一个单独的js文件,起名为:vueBus.js,然后里边写:
import Vue from 'vue'; let bus = new Vue(); export default bus;
2.在需要接收信息的地方注册监听事件:先引入bus
mounted(){ let self = this bus.$on('fromSecond',(data)=>{ self.res = data//获取到传过来的值 }); },
3.然后发送广播事件:先引入bus
sendMsg(){ bus.$emit("fromSecond",this.res) }
注意:先引入bus:
import bus from '@/api/vueBus.js'
并且发一次,多个注册过的地方都能接收到
--------------------------------------------------------------------------------------------------------------------------分割线
那实现原理是什么样的呢?当然知道了上边的内容完全够用了,但在这个人人装b的时代,你不了解原理怎么能够呢,原理你要知道并且深深的知道!
想知道原理就得看源码:
export function eventsMixin (Vue: Class<Component>) { const hookRE = /^hook:/ // 这里是注册事件 Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { const vm: Component = this if (Array.isArray(event)) {// 这里看到注册事件时候可以传个数组进来 for (let i = 0, l = event.length; i < l; i++) {//循环着添加事件 vm.$on(event[i], fn)//调用自己 } } else {// _events是装所有事件的对象 // 添加进_events去 如果对应的这个事件名已经有方法了就再往里边push一个 // 如果没有就建一个数组往里边push // 所以 你往数组里边放几次执行时候就会执行几次, // 同一个事件名 注册几个不同/相同回调方法 都会执行 (vm._events[event] || (vm._events[event] = [])).push(fn) // optimize hook:event cost by using a boolean flag marked at registration // instead of a hash lookup if (hookRE.test(event)) { vm._hasHookEvent = true } } return vm } // 这里是一次性绑定 Vue.prototype.$once = function (event: string, fn: Function): Component { const vm: Component = this // 封装一个fn 执行时候移除绑定 这样就实现只能触发一次了 function on () { vm.$off(event, on) fn.apply(vm, arguments) } on.fn = fn vm.$on(event, on)// 绑定封装过一次的fn return vm } // 移除绑定事件 Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { const vm: Component = this // all if (!arguments.length) { vm._events = Object.create(null) return vm } // array of events // 是数组的话 循环着去解绑 if (Array.isArray(event)) { for (let i = 0, l = event.length; i < l; i++) { vm.$off(event[i], fn) } return vm } // specific event const cbs = vm._events[event] if (!cbs) {// 找不到这个事件 你解绑个p呀 return vm } if (!fn) {// 解绑时候没有传过来 回调方法 你解绑个p呀 vm._events[event] = null return vm } // specific handler let cb let i = cbs.length while (i--) { cb = cbs[i] if (cb === fn || cb.fn === fn) { cbs.splice(i, 1)//找到他 并且移除他 break } } return vm } // 发送事件 Vue.prototype.$emit = function (event: string): Component { const vm: Component = this if (process.env.NODE_ENV !== 'production') { const lowerCaseEvent = event.toLowerCase() if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) { tip( `Event "${lowerCaseEvent}" is emitted in component ` + `${formatComponentName(vm)} but the handler is registered for "${event}". ` + `Note that HTML attributes are case-insensitive and you cannot use ` + `v-on to listen to camelCase events when using in-DOM templates. ` + `You should probably use "${hyphenate(event)}" instead of "${event}".` ) } } // 找到这个方法 let cbs = vm._events[event] if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs
// 把传过来的参数去掉第一个,转化成参数数组
const args = toArray(arguments, 1) const info = `event handler for "${event}"` for (let i = 0, l = cbs.length; i < l; i++) { // 执行这些方法 invokeWithErrorHandling(cbs[i], vm, args, vm, info) } } return vm } }
可以看到:添加事件时候会把方法放到Vue的_events对象上,这个对象的key是事件名,value是fn数组;你调用$emit时候,他会根据你传过来的事件名称去找到对应的fn数组,循环执行之。
看源码意外收获:
1. 注册事件时候事件名而已传数组
2. 一个/组事件名可以绑定不同的方法,执行时候按顺序一并执行
3. 解除事件监听时候需要传事件名和方法进来
4. 注册一次性事件是怎么完成的
over!