不要滥用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等状态管理工具,不仅简化了代码,还优化了项目体积,还让自己的代码更有逼格!相信大家看完一定有所收获!也欢迎大家能给出自己的建议,帮助他人哈!

往期精彩文章

相关推荐
迷雾漫步者1 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240254 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar4 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人5 小时前
前端知识补充—CSS
前端·css
GISer_Jing5 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试