前言
最近在做 大屏可视化
-
需求:监听屏幕尺寸变化对echarts进行重绘,确保数据可视化组件随窗口大小自动调整
-
- 使用
单例模式
,整个应用只会有一个窗口大小监听器,避免了性能
浪费
- 使用
-
- resize 事件使用 200ms
防抖处理
,避免频繁触发导致的性能
问题
- resize 事件使用 200ms
-
- 提供事件订阅机制让多个组件可以同时监听屏幕变化。
注意事项
-
- 用封装的第二个方法时,需确保在组件卸载时正确移除事件监听器
-
- 如需自定义防抖延迟时间,可以修改
handleResize
函数的第二个参数
- 如需自定义防抖延迟时间,可以修改
工具函数封装在 @/utils/screenManager.js
, 简单介绍如下:
-
常量:
SCREEN_CHANGE_EVENT
:屏幕变化事件名称常量,值为"screen-change"
-
类:
ScreenManager
:屏幕管理器类,提供以下方法:constructor()
:构造函数(单例模式)on(event, callback)
:注册事件监听器off(event, callback)
:移除事件监听器
-
实例属性:
screenWidth
:响应式屏幕宽度screenHeight
:响应式屏幕高度
-
工具函数:
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处理防抖
-
安装依赖:
bashnpm install lodash.debounce # 如果项目中尚未安装
-
注册插件 : 在
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>