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!
posted on 2019-12-19 17:10  rainbowLover  阅读(1591)  评论(0编辑  收藏  举报