Vue 中 $on $once $off $emit 详细分析,以及使用

vue的 $on,$emit,$off,$once

Api 中的解释:

  • $on(eventName:string|Array, callback) 监听事件

    • 监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。
  • $once(eventName: string, callback) 监听事件

    • 监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。
  • $off(eventName: string| Array, callback) 移除自定义事件监听器

      1. 如果没有提供参数,则移除所有的事件监听器;
    
      2. 如果只提供了事件,则移除该事件所有的监听器;
    
      3. 如果同时提供了事件与回调,则只移除这个回调的监听器。
    
  • 使用 $emit(eventName, [..args]) 触发事件

    • 触发当前实例上的事件。附加参数都会传给监听器回调

$on 实现原理

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
};

$once 实现原理

Vue.prototype.$once = function (event, fn) {
      var vm = this;
      function on () {
        vm.$off(event, on);
        fn.apply(vm, arguments);
      }
      on.fn = fn;
      vm.$on(event, on);
      return vm
    };

$off

Vue.prototype.$off = function (event, fn) {
      var vm = this;
      // all
      if (!arguments.length) {
        vm._events = Object.create(null);
        return vm
      }
      // array of events
      if (Array.isArray(event)) {
        for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {
          vm.$off(event[i$1], fn);
        }
        return vm
      }
      // specific event
      var cbs = vm._events[event];
      if (!cbs) {
        return vm
      }
      if (!fn) {
        vm._events[event] = null;
        return vm
      }
      // specific handler
      var cb;
      var i = cbs.length;
      while (i--) {
        cb = cbs[i];
        if (cb === fn || cb.fn === fn) {
          cbs.splice(i, 1);
          break
        }
      }
      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
    };

用法:监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

应用

  • 多次绑定,$on, $once 将多次调用,$off 调用一次将解绑所有相同名字的$on, $once 监听事件
<template>
  <div class="hello">
    <button @click="onEmit">OnEmit</button>
    <button @click="onOff">OnOff</button>
    <button @click="onOn">on</button>
  </div>
</template>

<script>
export default {
  name: 'Test',
  mounted() {

  },
  methods: {
    onEmit() {
      this.$emit('onTest', 1);
      this.$emit('onTest', 2)
      this.$emit('onTest', 3)
      this.$emit('onTest', 4)
      this.$emit('onTestOnce', 5);
      this.$emit('onTestOnce', 6);
      this.$emit('onTestOnce', 7);
      this.$emit('onTestOnce', 8);
    },
    onOff() {
      this.$off('onTest');
    },
    onOn(){
      this.$on('onTest', (args) => {
        console.log(args)
      })
      this.$once('onTestOnce', (args) => {
        console.log(args)
      })
    }
  }
}
</script>

子组件到父组件通讯

<hello @pfn="parentFn"></hello>

<script>
  Vue.component('hello', {
    template: '<button @click="fn">按钮</button>',
    methods: {
      // 子组件:通过$emit调用
      fn() {
        this.$emit('pfn', '这是子组件传递给父组件的数据')
      }
    }
  })
  new Vue({
    methods: {
      // 父组件:提供方法
      parentFn(data) {
        console.log('父组件:', data)
      }
    }
  })
</script>

非父子组件通讯

  • 组件 A.vue
<template>
  <div @click="onAon">
    组件a
  </div>
</template>
<script>
// import bus from "@/components/bus";
export default {
  name: 'A',
  methods: {
    onAon() {
      this.$bus.$emit('bTest')
    }
  }
}
</script>

组件B.vue

<template>
  <div>
    b 组件
  </div>
</template>
<script>
// import bus from "@/components/bus";
export default {
  mounted() {
    this.$bus.$on('bTest', () => {
      console.log('---- 触发b组件监听 ------')
    })
  }
}
</script>

  • bus.js
import Vue from "vue";

let bus = new Vue();
export default bus;

注:可以将bus 全局引入

posted @ 2021-12-02 20:21  影的记忆  阅读(3395)  评论(0编辑  收藏  举报