Vue3使用mitt进行组件通信

mitt:事件总线,是第三方插件。

Vue2.x 使用 EventBus 事件总线进行兄弟组件通信,而在Vue3中 EventBus 事件总线模式已经被移除,官方建议使用外部的、实现了事件触发器接口的库,例如 mitttiny-emitter

比起 Vue 实例上的 EventBus,mitt.js 好在哪里呢?

  1. 首先它足够小,仅有200bytes。
  2. 其次支持全部事件的监听和批量移除。
  3. 它还不依赖 Vue 实例,可以跨框架使用,React 或者 Vue,甚至 jQuery 项目都能使用同一套库。

1、安装 mitt

npm install --save mitt

2、在公共文件 utils 下新建 mitter.ts文件,简单封装下 mitt

utils/mitter.ts

import mitt from 'mitt';

const mitter = mitt();

export default mitter;

在这里插入图片描述
在这里插入图片描述

3、使用

utils/request.ts

如果登录过期,派发 token-expired 事件。
在这里插入图片描述

import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { showMessage } from './errorCode';
import { alertController } from '@ionic/vue';
import { showToast } from '.';
import { StorageService } from './storageService';
import mitter from './mitter';

let token: string = '';
// 是否显示重新登录
let isReloginShow: boolean = false;

const storageService = new StorageService();
storageService.create();

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
// 创建axios实例
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_ENV == 'development' ? import.meta.env.VITE_APP_BASE_API : import.meta.env.VITE_APP_BASE_URL, // 设置API的基础URL
  timeout: 10000,   // 设置请求超时时间
});

const presentAlert = async () => {

  const alert = await alertController.create({
    header: '系统提示',
    message: '登录状态已过期,您可以继续留在该页面,或者重新登录',
    buttons: [
      {
        text: '取消',
        role: 'cancel',
        handler: () => {
          console.log('Alert canceled');
          isReloginShow = false;
        },
      },
      {
        text: '重新登录',
        role: 'confirm',
        handler: () => {
          console.log('Alert confirmed');
          isReloginShow = false;
          mitter.emit('token-expired');
        },
      },
    ],
  });

  await alert.present();
};

// 请求拦截器
service.interceptors.request.use(
  async (config: AxiosRequestConfig): Promise<any> => {
    // showLoading();
    // 是否需要设置 token    
    const isToken = (config.headers || {}).isToken === false
    const headers = config.headers || {};
    token = await storageService.getItem("token");
    if (token && !isToken) {
      headers['Authorization'] = 'Bearer ' + token; // 让每个请求携带自定义token 请根据实际情况自行修改
      // headers['token'] = token;
      config.headers = headers;
    }

    // return new Promise((resolve, reject) => {
    //   resolve(config)
    // })
    return config;
  },
  (error: AxiosError) => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    // 对响应数据进行处理
    // 未设置状态码则默认成功状态
    const code = response.data.code || 200;
    // 获取错误信息
    const msg = response.data.msg || showMessage(code) || showMessage('default');
    // 二进制数据则直接返回
    if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
      return response.data
    }
    if (code === 401) {
      if (!isReloginShow) {
        isReloginShow = true;
        presentAlert();
      }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      showToast(msg);
      return Promise.reject(new Error(msg))
    } else if (code !== 200) {
      showToast(msg);
      return Promise.reject('error')
    } else {
      return response.data
    }
    // return response;
  },
  (error: AxiosError) => {
    // 处理响应错误
    return Promise.reject(error);
  }
);

export {
  service as request
}

src/HomePage.vue

在首页监听 token-expired 登录过期事件, 在离开页面时移出监听事件。

在这里插入图片描述

<script setup lang="ts">
import {
  IonPage,
  IonSearchbar,
  IonList,
  IonItem,
  IonLabel,
  IonSegment,
  IonSegmentButton,
  IonIcon,
  onIonViewWillEnter,
  onIonViewDidEnter,
  onIonViewWillLeave,
  modalController,
  IonNote,
  IonContent,
  IonHeader,
} from "@ionic/vue";
import { ref, inject } from "vue";
import * as echarts from "echarts";
import { StorageService } from "@/utils/storageService";
import { getAreaSituation, getCountrySamplePie, getNotify, getSampleInfo, getSampleInfoList } from "@/api/home";
import mitter from "@/utils/mitter";
import { useRouter } from "vue-router";
import HomeSearchDetails from "@/components/HomeSearchDetails.vue";
import { chevronForwardOutline } from "ionicons/icons";
import { QRScanner, registerKeyCodeReceiver, unregisterReceiver, UHFNotifyListeners, UHFRemoveAllListeners, addListenerRate, getEPC } from "@/utils/ratePlugin";
import { showToast } from "@/utils";

const router = useRouter();
const keyWords = ref<string | undefined>("");
const areaList = ref<any[]>([]);
const selectedArea = ref<any>(null);
const inventoryStatisticsPieChart = ref<HTMLElement | null>(null);
let pieChart: echarts.ECharts;
const seriesData = ref<any[]>([]);
const messageList = ref<string[]>([]);
const searchList = ref<any[]>([]);
const storageService = inject("storageService") as StorageService;

onIonViewWillEnter(async () => {
  const token = await storageService.getItem("token");
  const userInfo = await storageService.getItem("userInfo");
  // console.log(token, "首页token", JSON.parse(userInfo));
  getAreaList();
  getNotifyData();
});

onIonViewDidEnter(() => {
  window.addEventListener("resize", handleResize);
  mitter.on("token-expired", () => {
    // console.log("token失效,跳转到登录页面");
    router.push("/login");
  });
  getChartData();
});

onIonViewWillLeave(() => {
  window.removeEventListener("resize", handleResize);
  if (pieChart) {
    pieChart.dispose();
  }
  mitter.off("token-expired");

});

const handleInput = (event: Event) => {
  // keyWords.value = (event.target as HTMLInputElement).value;
  // console.log("搜索:", keyWords.value);
  searchList.value = [];
  if (keyWords.value) {
    getSampleInfo(keyWords.value).then((res: any) => {
      if (res.code === 200) {
        searchList.value = res.data;
      }
    })
  }
};

const segmentChange = (event: Event) => {
  let val: string = (event.target as HTMLInputElement).value;
  selectedArea.value = areaList.value.find((item) => item.areaName == val);
};

const initChart = () => {
  pieChart = echarts.init(inventoryStatisticsPieChart.value);
  let option = {
    title: {
      text: "99865",
      subtext: "库存总量",
      left: "14%",
      top: "center",
      textStyle: {
        fontSize: 20,
      },
      subtextStyle: {
        fontSize: 18,
      },
    },
    tooltip: {
      trigger: "item",
    },
    legend: {
      top: "center",
      type: "scroll",
      right: 0,
      orient: "vertical",
      formatter: (name: string) => {
        let total = 0;
        let target = 0;
        seriesData.value.forEach((item) => {
          total += item.value;
          if (item.name == name) {
            target = item.value;
          }
        });
        let percent = total ? ((target / total) * 100).toFixed(2) : 0;
        return `${name}  ${percent}% ${target}`;
      },
    },
    series: [
      {
        // name: "Access From",
        type: "pie",
        radius: ["70%", "90%"],
        right: "50%",
        avoidLabelOverlap: false,
        label: {
          show: false,
          position: "center",
        },
        labelLine: {
          show: false,
        },
        data: seriesData.value,
      },
    ],
  };

  option && pieChart.setOption(option);
};

const handleResize = () => {
  setTimeout(() => {
    if (pieChart) {
      pieChart.resize();
    }
  }, 500);
};

const getAreaList = () => {
  getAreaSituation().then((res: any) => {
    if (res.code === 200) {
      areaList.value = res.data;
      selectedArea.value = areaList.value[0];
    }
  });
};

const getChartData = () => {
  getCountrySamplePie().then((res: any) => {
    if (res.code === 200) {
      seriesData.value = res.data;
      initChart();
    }
  });
};

const getNotifyData = () => {
  getNotify().then((res: any) => {
    if (res.code === 200) {
      messageList.value = res.data;
    }
  });
};

</script>

参考:Vue3 mitt 组件通信 - 附完整示例

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值