不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!

前言

vue2的vuex、和vue3的Pinia或者react的Redux,相信大家一定不陌生!在很多情况下,两个几乎不关联的组件之间通信,我们大多都会使用这些状态管理工具完成。

假设有下面的一个场景,我们需要再C组件 点击一个按钮,请求一个数据,然后在发送给E组件,E组件执行一些逻辑处理。

如果我们的系统比较庞大,模块众多,选择Pinia是一个非常便捷的方式!但如果我们整个系统比较小,不同模块之间交互的情况不多,为了一个简单的功能引入Pinia是非常不划算的!

为了完成上面的需求,我们的代码必须这么写

首先,我们需要安装和设置Pinia ,然后,我们创建一个 Pinia store 来管理数据。

js 复制代码
// store.js
import { createPinia } from 'pinia';

const pinia = createPinia();

export const useDataStore = pinia.store('data', {
  state: () => ({
    data: null,
  }),
  actions: {
    fetchData() {
      // 在这里发起接口请求获取数据,这里使用一个假的示例数据
      const fakeData = '这是从接口获取的数据';
      this.data = fakeData;
    },
  },
});

export default pinia;

接下来,我们创建 C 组件来触发数据请求。

js 复制代码
<template>
  <button @click="fetchData">请求数据</button>
</template>

<script>
import { defineComponent } from 'vue';
import { useDataStore } from './store';

export default defineComponent({
  setup() {
    const dataStore = useDataStore();

    const fetchData = () => {
      dataStore.fetchData();
    };

    return {
      fetchData,
    };
  },
});
</script>

最后,我们创建 E 组件来接收数据并进行逻辑处理。

js 复制代码
<template>
  <div>
    <p v-if="data">{{ data }}</p>
    <p v-else>暂无数据</p>
  </div>
</template>

<script>
import { defineComponent } from 'vue';
import { useDataStore } from './store';

export default defineComponent({
  setup() {
    const dataStore = useDataStore();

    // 从 store 中获取数据
    const data = dataStore.data;

    return {
      data,
    };
  },
});
</script>

我们仅仅为了一个很小的功能,引入了Pinia,非常不划算!费事费力!

使用事件调度器优化代码

假设我们现在有个事件调度器,可以实现事件绑定事件监听,它的用法是这样的

js 复制代码
const dispatcher = new EventDispatcher()

// 绑定事件
dispatcher.addEventListener("patcher1",(res) => {
  // 监听到的数据
  console.log(res);
})

dispatcher.dispatchEvent('patcher1',"发送的数据")

我们用这个dispatcher优化上述的逻辑代码

js 复制代码
/* C组件 */
<template>
  <button @click="fetchData">请求数据</button>
</template>

<script setup>
const fetchData = () => {
  dispatcher.dispatchEvent('patcher1',"发送的数据")
};
</script>
js 复制代码
<template>
  <div>
    <p v-if="data">{{ data }}</p>
    <p v-else>暂无数据</p>
  </div>
</template>

<script>
const data = ref("");

// 绑定事件
dispatcher.addEventListener("patcher1",(res) => {
  // 监听到的数据
  data.value = res
})
</script>

可以看到,同样的逻辑,我们使用dispatcher代码更加简洁!避免了引入pinia,不仅减少了代码体积,还让代码写起来更加简单!

可见,一些简单的场景,如整个系统之间只有个别组件之间存在交互,引入pinia或者redux等状态管理工具是非常不划算的!!

你可能会有疑惑,不是需要引入EventDispatcher这个类吗?这个类难道不复杂?体积不大?

事实上,这个类的代码也就十几行,完全可以手写,以后粘贴复制!

实现EventDispatcher事件调度器

我们这个EventDispatcher其实功能很简单,就是事件监听事件绑定移除事件三个功能。

它的使用方法应该如下

js 复制代码
const dispatcher = new EventDispatcher()

// 绑定第一个一个事件
dispatcher.addEventListener("patcher1",(res) => {
  // 监听到的数据
  console.log(res);
})

// 绑定第二个事件
dispatcher.addEventListener("patcher2",(res) => {
  // 监听到的数据
  console.log(res);
})

// 触发第一个事件
dispatcher.dispatchEvent('patcher1',"发送的数据")
// 触发第二个事件
dispatcher.dispatchEvent('patcher2',"发送的数据")

//移除第一个事件
dispatcher.removeEventListener('patcher1')
//移除第二个事件
dispatcher.removeEventListener('patcher2')

要想实现这个功能,我们需要创建一个事件调度器EventDispatcher类,这个类的结构如下

js 复制代码
export class EventDispatcher  {
    // 储存所有监听的事件
    private listeners = {};
    // 绑定事件的方法
    protected addEventListener(type: string, listener: Function) {
        
    }
    // 移除事件的方法
    protected removeEventListener(type: string) {
        
    }
    // 触发事件的方法
    protected dispatchEvent(type: string, data: any) {
        
    }
}

我们完善其内部方法

js 复制代码
export class EventDispatcher{
    private listeners: { [type: string]: Function[] } = {};

    protected addEventListener(type: string, listener: Function) {
        if (!this.listeners[type]) {
            this.listeners[type] = [];
        }
        if (this.listeners[type].indexOf(listener) === -1) {
            this.listeners[type].push(listener);
        }
    }
    protected removeEventListener(type: string) {
        this.listeners[type] = [];
    }

    protected dispatchEvent(type: string, data: any) {
        const listenerArray = this.listeners[type] || [];
        if (listenerArray.length === 0) return;
        listenerArray.forEach(listener => {
            listener.call(this, data);
        });
    }
}
  • listeners用于存储事件类型与对应监听器数组的映射。它是一个对象,键是事件类型(type),值是监听器函数数组。
  • addEventListener用于添加事件监听器。它接受两个参数:事件类型(type)和监听器函数(listener)。如果当前事件类型尚未有对应的监听器数组,则创建一个空数组。然后,将传入的监听器函数添加到该数组中,但仅当该监听器函数不在数组中时才添加。
  • dispatchEvent用于触发指定类型的事件,并传递相应的数据。它接受两个参数:事件类型(type)和要传递的数据(data)。首先,它从存储的监听器数组中获取与事件类型匹配的监听器数组。然后,遍历该数组,依次调用每个监听器函数,并传递数据作为参数。
  • removeEventListener用于移除指定类型的所有事件监听器。它接受一个参数:事件类型(type)。当调用该方法时,它会将指定类型的监听器数组清空。

上述代码实现了一个简单的事件调度器,它的核心原理是使用了一个对象 listeners 来存储事件类型和对应的监听器函数数组。当调用 addEventListener 方法添加监听器时,它会将监听器函数添加到对应事件类型的监听器数组中。当调用 dispatchEvent 方法触发事件时,它会获取对应事件类型的监听器数组,并依次调用每个监听器函数。而 removeEventListener 方法则用于移除指定类型的所有监听器。

如果你想在全局任意的地方使用这个数据调度器,我们只需要将它绑定在window对象上即可!

在我们的程序入口文件里进行挂载

js 复制代码
const dispatcher = new EventDispatcher()
window.myDispatcher = dispatcher

现在,你可以在任何地方开心的使用这个方法了!简单的场景再也不用引入麻烦的pinia或者redux了!

总结

本教程中,基于简单业务不同组件之间交互的场景,我们使用手写的事件调度器EventDispatcher替代了pinia或者redux等状态管理工具,不仅简化了代码,还优化了项目体积,还让自己的代码更有逼格!相信大家看完一定有所收获!也欢迎大家能给出自己的建议,帮助他人哈!

往期精彩文章

相关推荐
热爱编程的小曾9 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin20 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox33 分钟前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox