在 vue 中优雅封装 resize 事件[单例模式、订阅机制、防抖...]

前言

最近在做 大屏可视化

  • 需求:监听屏幕尺寸变化对echarts进行重绘,确保数据可视化组件随窗口大小自动调整

    1. 使用单例模式,整个应用只会有一个窗口大小监听器,避免了性能浪费
    1. resize 事件使用 200ms 防抖处理,避免频繁触发导致的性能问题
    1. 提供事件订阅机制让多个组件可以同时监听屏幕变化。

注意事项

    1. 用封装的第二个方法时,需确保在组件卸载时正确移除事件监听器
    1. 如需自定义防抖延迟时间,可以修改 handleResize 函数的第二个参数

工具函数封装在 @/utils/screenManager.js, 简单介绍如下:

  1. 常量

    • SCREEN_CHANGE_EVENT:屏幕变化事件名称常量,值为 "screen-change"
    • ScreenManager:屏幕管理器类,提供以下方法:
      • constructor():构造函数(单例模式)
      • on(event, callback):注册事件监听器
      • off(event, callback):移除事件监听器
  2. 实例属性

    • screenWidth:响应式屏幕宽度
    • screenHeight:响应式屏幕高度
  3. 工具函数

    • screenManager:预创建的单例实例
    • ScreenManagerPlugin:Vue 插件,用于全局注册
    • useScreenManager():组合式函数,用于在组件中获取实例

源码

javascript 复制代码
// utils/screenManager.js
import { ref, onMounted, onUnmounted, createApp, provide, inject } from "vue";
// import { debounce } from "@/utils"; // 导入防抖工具函数,避免频繁触发resize事件
import debounce from 'lodash.debounce'; // 导入防抖工具函数,避免频繁触发resize事件
 
/**
 * 屏幕管理工具 - 用于监听窗口尺寸变化并提供响应式数据
 * 使用单例模式确保整个应用中只有一个监听器实例
 */

// 单例实例存储
let screenManagerInstance = null;

// 屏幕变化事件常量,用于统一事件名称管理
export const SCREEN_CHANGE_EVENT = "screen-change";

/**
 * 屏幕管理器类
 * 负责监听窗口大小变化,维护屏幕尺寸数据,并通知订阅者
 */
export class ScreenManager {
  constructor() {
    // 实现单例模式 - 如果已存在实例则直接返回
    if (screenManagerInstance) return screenManagerInstance;

    // 响应式数据:屏幕宽度和高度
    this.screenWidth = ref(window.innerWidth);
    this.screenHeight = ref(window.innerHeight);

    // 存储事件监听器的集合
    this.eventListeners = new Set();

    // 使用防抖处理resize事件,避免频繁触发(默认200ms)
    this.handleResize = debounce(() => {
      const width = window.innerWidth;
      const height = window.innerHeight;

      // 仅在尺寸真正变化时更新数据并通知订阅者
      if (
        width !== this.screenWidth.value ||
        height !== this.screenHeight.value
      ) {
        this.screenWidth.value = width;
        this.screenHeight.value = height;

        // 通知所有订阅者屏幕尺寸已变更
        this.eventListeners.forEach((listener) => {
          listener({ width, height });
        });
      }
    }, 200);

    // 生命周期钩子:组件挂载时添加resize事件监听
    onMounted(() => {
      window.addEventListener("resize", this.handleResize);
    });

    // 生命周期钩子:组件卸载时移除resize事件监听,防止内存泄漏
    onUnmounted(() => {
      window.removeEventListener("resize", this.handleResize);
    });

    // 保存当前实例为单例
    screenManagerInstance = this;
    return this;
  }

  /**
   * 注册事件监听器
   * @param {string} event - 事件名称,使用SCREEN_CHANGE_EVENT常量
   * @param {Function} callback - 屏幕变化时的回调函数,接收尺寸对象 { width, height }
   */
  on(event, callback) {
    if (event === SCREEN_CHANGE_EVENT) {
      this.eventListeners.add(callback);
    }
  }

  /**
   * 移除事件监听器
   * @param {string} event - 事件名称,使用SCREEN_CHANGE_EVENT常量
   * @param {Function} callback - 需要移除的回调函数
   */
  off(event, callback) {
    if (event === SCREEN_CHANGE_EVENT) {
      this.eventListeners.delete(callback);
    }
  }
}

// 创建并导出单例实例,整个应用共享同一个屏幕管理器
export const screenManager = new ScreenManager();

/**
 * Vue插件实现,用于在组件中便捷使用屏幕管理器
 * 通过provide/inject机制提供全局访问
 */
export const ScreenManagerPlugin = {
  install(app) {
    app.provide("screenManager", screenManager);
  },
};

/**
 * 组合式函数,用于在setup函数中获取屏幕管理器实例
 * @returns {ScreenManager} 屏幕管理器实例
 */
export function useScreenManager() {
  return inject("screenManager") || screenManager;
}

使用文档

一、安装依赖与配置

项目使用到了lodash处理防抖

  1. 安装依赖

    bash 复制代码
    npm install lodash.debounce  # 如果项目中尚未安装
  2. 注册插件 : 在 main.js)中注册插件:

    javascript 复制代码
    // ....省略部分内容
    // -------- 关键注册 --------
    import { ScreenManagerPlugin } from "@/utils/screenManager";
    
    const app = createApp(App);
    app.use(ScreenManagerPlugin); // 注册屏幕管理插件
    app.mount("#app");

三、使用方式

ScreenManager 提供了两种主要的使用方式:

方式一:使用 useScreenManager 组合式函数

vue 复制代码
<template>
  <div>
    <p>当前屏幕宽度: {{ screenWidth }}</p>
    <p>当前屏幕高度: {{ screenHeight }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from "vue";
import { useScreenManager } from "@/utils/screenManager";

const screenManager = useScreenManager();
const screenWidth = ref(screenManager.screenWidth.value);
const screenHeight = ref(screenManager.screenHeight.value);

// 使用watch监听屏幕尺寸变化
watch(
  () => [screenManager.screenWidth.value, screenManager.screenHeight.value],
  ([width, height]) => {
    screenWidth.value = width;
    screenHeight.value = height;
    console.log(`屏幕尺寸已变更为: ${width} x ${height}`);

    // 在这里可以添加响应式逻辑
    if (width < 768) {
      console.log("当前处于移动设备视图");
    }
  },
  { immediate: true } // 立即执行一次回调
);
</script>

方式二:通过.on/.off事件监听

JavaScript 复制代码
<template>
  <div>
    <p>当前屏幕宽度: {{ screenWidth }}</p>
    <p>当前屏幕高度: {{ screenHeight }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import { useScreenManager, SCREEN_CHANGE_EVENT } from "@/utils/screenManager";

const screenManager = useScreenManager();
const screenWidth = ref(window.innerWidth);
const screenHeight = ref(window.innerHeight);

// 定义屏幕变化的回调函数
const handleScreenChange = ({ width, height }) => {
  screenWidth.value = width;
  screenHeight.value = height;
  console.log(`屏幕尺寸变更为: ${width} x ${height}`);

  // 可添加自定义响应式逻辑
  if (width < 768) {
    // 执行移动端适配逻辑
  }
};

// 生命周期钩子:组件挂载时注册事件监听
onMounted(() => {
  screenManager.on(SCREEN_CHANGE_EVENT, handleScreenChange);
});

// 生命周期钩子:组件卸载时移除事件监听,防止内存泄漏
onUnmounted(() => {
  screenManager.off(SCREEN_CHANGE_EVENT, handleScreenChange);
});
</script>
相关推荐
华玥作者15 小时前
[特殊字符] VitePress 对接 Algolia AI 问答(DocSearch + AI Search)完整实战(下)
前端·人工智能·ai
Mr Xu_15 小时前
告别冗长 switch-case:Vue 项目中基于映射表的优雅路由数据匹配方案
前端·javascript·vue.js
前端摸鱼匠15 小时前
Vue 3 的toRefs保持响应性:讲解toRefs在解构响应式对象时的作用
前端·javascript·vue.js·前端框架·ecmascript
lang2015092816 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
好家伙VCC16 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
未来之窗软件服务17 小时前
未来之窗昭和仙君(六十五)Vue与跨地区多部门开发—东方仙盟练气
前端·javascript·vue.js·仙盟创梦ide·东方仙盟·昭和仙君
嘿起屁儿整17 小时前
面试点(网络层面)
前端·网络
VT.馒头17 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
phltxy18 小时前
Vue 核心特性实战指南:指令、样式绑定、计算属性与侦听器
前端·javascript·vue.js
Byron070719 小时前
Vue 中使用 Tiptap 富文本编辑器的完整指南
前端·javascript·vue.js