<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="btn">click</button>

    <script>
      function Vue() {}

      eventsMixin(Vue);

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

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

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

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

      /**
       * Convert an Array-like object to a real Array.
       */
      function toArray(list, start) {
        start = start || 0;
        var i = list.length - start;
        var ret = new Array(i);
        while (i--) {
          ret[i] = list[i + start];
        }
        return ret;
      }

      function invokeWithErrorHandling(handler, context, args, vm, info) {
        var res;
        try {
          res = args ? handler.apply(context, args) : handler.call(context);
          if (res && !res._isVue && isPromise(res) && !res._handled) {
            res.catch(function (e) {
              return handleError(e, vm, info + " (Promise/async)");
            });
            // issue #9511
            // avoid catch triggering multiple times when nested calls
            res._handled = true;
          }
        } catch (e) {
          handleError(e, vm, info);
        }
        return res;
      }
    </script>

    <script>
      let v = new Vue();
      v._events = Object.create(null);

      let btn = document.getElementById("btn");
      btn.addEventListener("click", function () {
        v.$emit("test", 1, 2, 3);
      });

      v.$on("test", function (a, b, c) {
        console.log(a, b, c);
        alert(a, b, c)
      });
    </script>
  </body>
</html>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.