全栈-vue-$on与$emit

535 阅读1分钟

$ on emit off

定义(是什么)

  • $on 订阅消息 (监听实例上的某个具体事件)
  • $once 只订阅一次(尤其适用场景)
  • $emit 发布消息(触发实例上的某个具体事件)
  • $off 取消订阅消息
  • xxx

场景

  1. v-on 与 $emit 父子组件中的 事件传递

    父组件中
    v-on:myEvent="handleEvent" // 监听子组件中的 myEvent消息
    子组件中
    this.$emit("myEvent",args)
    
  2. $on 对第三方组件 内部事件的拦截优点:不对原来的组件做任何修改

    // eg 对elementUI等三方组件的内部方法改造与拦截
    this.$refs['form'].$on("change",()=>{})
    
  3. on 与 emit 配合公共组件使用,也就是 eventBus,那么就能实现 全局的消息发送,贯穿全局

    this.eventBus.on("myEvent",()=>{})
    this.eventBus.emit("myEvent","msg")
    
  4. on 与 emit 配合使用,自己封装组件的定义 自定义事件

    ....
    
  5. xxx

设计模式:订阅者与发布者模式

经过研究源码解读,实现原理其实就是 订阅者与发布者模式

  1. 源码如下

    • on的源码

        function eventsMixin (Vue) {
          var hookRE = /^hook:/;
          Vue.prototype.$on = function (event, fn) {
            var vm = this;
            if (Array.isArray(event)) {
              for (var i = 0, l = event.length; i < l; i++) {
                vm.$on(event[i], fn);
              }
            } else {
              (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
          };
      
    • emit的源码

          Vue.prototype.$emit = function (event) {
            var vm = this;
            {
              var 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 + "\"."
                );
              }
            }
            var cbs = vm._events[event];
            if (cbs) {
              cbs = cbs.length > 1 ? toArray(cbs) : cbs;
              var args = toArray(arguments, 1);
              var info = "event handler for \"" + event + "\"";
              for (var i = 0, l = cbs.length; i < l; i++) {
                invokeWithErrorHandling(cbs[i], vm, args, vm, info);
              }
            }
            return vm
          };
        }
      
  2. 代码学习(学习源码过程中,发现有几点写的很好,记录如下

    • 字典或则对象 中的属性判空并初始化

      (vm._events[event] || (vm._events[event] = [])).push(fn);
      
    • try catch 的封装

      // fn 函数 ctx,this 对象,args 参数
      function invokeWithErrorHandling(fn,ctx,args){
      	var res 
      	try{
        	res = args ? fn.apply(ctx,args) : fn.call(args);
      	}.catch(e){
      			handleError(e) // js 内部函数
      	}
      	return res
      }
      
  3. ing...