Mitt is a miniature EventEmitter library. In Vue3, it is officially recommended to replace EventBus that has been removed.

Pig with qi hand grain, we are working people 🤣.

This time let’s take a look at mitt, a small but beautiful tool library, following my previous article on Vue3 cross-group communication. It’s small and beautiful because it’s less than 20 lines of source code, less than 200 bytes compressed, and we can learn design patterns and tricks from it that fill our bases nicely. Next, let’s start with examples and enter mitt’s small world!

Basically every line of code has comments, expect every friend can understand. Of course, the author itself is only a primary front-end, which if there is what to say wrong, also please big guys point out, thanks 🙈.

Initializing the Environment

Project version

  • Scaffolding: @vue/cli 4.5.6
  • Vue: ^ 3.0.0
  • Mitt: ^ 2.1.0

The project structure

Files that need attention

-src -- Components -- Level2.vue child -- Level3.vue child -- Assets -- Mitt.js Mitt source -- Views -- Level1.vue parent --main.jsCopy the code

Download the case

git clone https://github.com/taosiqi/vue3-mitt.git
cd vue3-mitt
yarn 
yarn serve
Copy the code

Case analysis

mian.js

import mitt from './assets/mitt' // add mitt library
const emitter =new mitt(); // instantiate mitt
const app = createApp(App);
app.config.globalProperties.$emitter = emitter; // Mount to global
app.use(store).use(router).mount('#app')
Copy the code

on && emit

First, we see the Level3.vue file. We get the component instance via getCurrentInstance, and then get the current context via proxy. We subscribe to an event using proxy.$emitter. On. It takes two parameters, the first being the name of the subscribed event and the second the callback function. Event names are also divided into two types, one is to subscribe to a specific event, and the other is to subscribe to all events, as shown in the code (line 28).

<template>
  <div class="level3">
    <div>level3</div>
    <button>Str1 value: {{str1}}</button>
    <button>Str2 value: {{str2}}</button>
    <button>Str3 value: {{str3}}</button>
  </div>
</template>

<script>
import {getCurrentInstance, ref} from 'vue'; // Get the component instance
export default {
  name: 'level3'.setup() {
    const {proxy} = getCurrentInstance();
    let str1 = ref(1)
    let str2 = ref(1)
    let str3 = ref(1)
    function str3Fn(e) {
      str1.value++
    }
    The on method is used to subscribe to an event. The first argument is the event name. The second argument is the callback function.
    proxy.$emitter.on('str1', str3Fn);
    proxy.$emitter.on('str2'.e= > {
      str2.value++
    });
    //on(*), subscribe to all events, and any emit will trigger its execution. Its callback takes two arguments, the first being the name of the published event and the second being the passed argument.
    proxy.$emitter.on(The '*'.(e,data) = > {
      console.log(e,data)
      str3.value++
    });
    proxy.$emitter.on('clearStr1'.e= > {
      // off Unsubscribe. Do not pass in an anonymous function for the second parameter. Otherwise, unsubscribe will fail.
      proxy.$emitter.off('str1', str3Fn)
    });
    return{ str1, str2, str3, }; }},</script>

Copy the code

Take another look at the Level1.vue file. We can publish an event using proxy.$emitter. Emit that takes two parameters, the first being the name of the event to subscribe to and the second being the parameter to pass (line 23).

<template>
  <div class="level1">
    <div>level1</div>
    <button @click="clickOneFn">Change level3 - > str1</button>
    <button @click="clickTwoFn">Change level3 - > str2</button>
  </div>
  <level2 msg="level2"/>
</template>

<script>
import level2 from '@/components/level2.vue'
import {getCurrentInstance} from 'vue'; // Get the component instance
export default {
  name: 'level1'.components: {
    level2
  },
  setup() {
    const {proxy} = getCurrentInstance();

    function clickOneFn() {
      // Emit is used to publish an event with the first argument being the subscription name and the second argument being the argument to be passed
      proxy.$emitter.emit('str1', {data: 'change the str1'});
    }

    function clickTwoFn() {
      proxy.$emitter.emit('str2', {data: 'change str2});
    }

    return{clickOneFn, clickTwoFn}; }}</script>

Copy the code

At this point, we can combinelevel1.vueandlevel2.vueLet’s see what happens in practiceThe str3 variable in Level3 has changed since I only clicked the first button and emitted str1. The reason for this is that we also use on(‘*’), which responds to any emit, whether it is emit(‘str1’) or emit(‘str2’), causing it to execute once.

proxy.$emitter.on(The '*'.(e,data) = > {
      console.log(e,data)
      str3.value++
});
Copy the code

off && clear

$emitter. All. Clear removes all subscribers and $emitter. Off removes individual subscribers. It is important to note that indexOf is used inside off to determine whether the callback exists or not. So if we have a need to remove, we should not pass in the anonymous function for the second parameter, otherwise unsubscribe will fail.

//level2
<template>
  <div class="level2">
    <div>level2</div>
    <button @click="clearAllFn">Use clear to clear all</button>
    <button @click="clearOneFn">Use off to clear a single</button>
  </div>
  <level3 msg='level3'></level3>
</template>

<script>
import { getCurrentInstance } from 'vue'; // Get the component instance
import level3 from '@/components/level3.vue'
export default {
  name: 'level2'.components: {
    level3
  },
  setup() {
    const { proxy } = getCurrentInstance();
    Clear is a Map method used to remove all key/value pairs from a Map
    function clearAllFn(){
      proxy.$emitter.all.clear()
    }
    // Unsubscribe specified subscriber, see Level3 file for details of unsubscribe code.
    function clearOneFn(){
      proxy.$emitter.emit('clearStr1');
    }
    return{ clearAllFn,clearOneFn }; }},</script>
Copy the code
// Level3 part of the code<script>
import {getCurrentInstance, ref} from 'vue'; // Get the component instance
export default {
  name: 'level3'.setup() {
    const {proxy} = getCurrentInstance();
    let str1 = ref(1)
    function str3Fn(e) {
      str1.value++
    }
    proxy.$emitter.on('clearStr1'.e= > {
      // off Unsubscribe. Do not pass in an anonymous function for the second parameter. Otherwise, unsubscribe will fail.
      proxy.$emitter.off('str1', str3Fn)
    });
    return{ str1, }; }},</script>
Copy the code

conclusion

By now, everyone must have a basic understanding of mitt’S API. The commonly used APIS are on and EMIT. If you have what what do not understand, you can download the case to run, the code is not much, API is simple, I believe that we can understand 😁😁. Let’s start our source code parsing. Hah.

The source code parsing

The source code

Mitmitt. Js is the source code I extracted from the official library. Have you found that there are few codes, less than 20 lines?

module.exports = function (n) {
  // Publish subscribe mode
  return {
    /** * instantiates a Map structure of n to manage subscribers */
    all: (n = n || new Map()),
    /** * the on method is used to subscribe to an event *@param The event name * e (string | symbol)@param T Function Callback method */
    on: function (e, t) {
      var i = n.get(e); The get method is used to return the value of the key, or undefined if none exists.
      / * * * * if I is undefined, in two different conditions, (I && i.p ush (t)) is false, perform | | right, n with set of key-value pairs. * else I is not undefined, then push t method into I. This is why clicking emit will be executed multiple times after the project is hot updated */
      (i && i.push(t)) || n.set(e, [t]);
    },
    /** * off Unsubscribe from specified subscriber *@param The event name * e (string | symbol)@param T Function Specifies the callback Function to be removed
    off: function (e, t) {
      var i = n.get(e);
      /** * if I is undefined, the subscriber that does not exist is passed in. * else use splice to delete. * if = -1, (i.dexof (t) >>> 0)===4294967295, splice will not intercept if else.  * Note that the second parameter does not use an anonymous function (arrow function), the two anonymous functions are not the same memory address, indexOf is strong equal judgment, will cause unsubscriber failure. * The unsigned right shift operator - > https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_s hift */
      i && i.splice(i.indexOf(t) >>> 0.1);
    },
    /** * emit is used to publish an event *@param The event name * e (string | symbol)@param T Any Passes the parameter */
    emit: function (e, t) {
      /** * Publish event, need to handle two cases,1: publish the event named e, 2: publish (*) event. They passed parameters is not the same as * (n.g et (e) | | []) get () function returns the value of the key, if it is undefined, it returns an array, prevent slice error. Slice returns a new array, the original array will not change, slice has two parameters, both of which are optional. * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice. * map loops through the method and passes the parameter t */
      (n.get(e) || []).slice().map(function (n) {
        n(t);
      }),
        (n.get("*") || []).slice().map(function (n) { n(e, t); }); }}; };Copy the code

conclusion

The mitt library, which is beautiful and small, has design patterns, some small skills and the application of operators, which are worth us to learn, and some small pits encountered in the use can make our foundation more stable. I hope you can move your hands and click “like” before you go, your support is the biggest encouragement to me!!

reference

  1. Array.slice
  2. > > > operator
  3. Mitt official library

To change the