Vue3中非响应式的全局状态管理

75 阅读2分钟

vue项目中,一般说到状态管理,我们会想到pinia,它可以帮助我们管理需要在多个页面、组件间共享的数据,并且根据数据的更新触发相关的渲染更新。但如果是数据变化不会引起页面刷新的全局数据呢?

 比如我当前开发的项目中,需要在项目初始化之后获取对应的引擎实例,该实例提供相关api用于处理页面逻辑,但该实例并不会触发页面的更新,此时就需要一个 非响应式的全局状态管理 -- globalState

适用场景:
  • 全局配置、缓存、临时数据
  • 跨页面/组件的事件通知
  • 不需要响应式的数据共享
不适用场景
  • 需要数据变化时自动刷新页面的场景【用PiniaVuex
逻辑梳理:
  1.  定义一个globalState类,全局只有一个实例
  2. 维护一份state数据,提供setgethasdeleteclear
  3. 提供事件总线功能,用于在不同组件间“广播消息”: on【监听事件】、emit【触发事件】、off【移除监听】

具体实现代码:

/stores/globalState.ts

// 全局状态管理(非响应式)
/**
 * 跨页面/组件共享数据,但又不需要响应式(不需要自动刷新UI)。
 * 存储全局配置、缓存、临时数据等。
 * 避免污染 window,比直接用 window.xxx 更安全、可控、易维护。
 * 比 Pinia/Vuex 更轻量,适合存储不需要响应式的数据。
 */

class GlobalState {
    private static instance: GlobalState
    private state: Map<string, any> = new Map()
    private eventListeners: Map<string, Function[]> = new Map()

    static getInstance(): GlobalState {
        if(!GlobalState.instance) {
            GlobalState.instance = new GlobalState()
        }
        return GlobalState.instance
    }
    
    set(key: string, value: any): void {
        this.state.set(key, value)
    }

    get(key: string): any {
        return this.state.get(key)
    }

    has(key: string): boolean {
        return this.state.has(key)
    }

    delete(key: string): boolean {
        return this.state.delete(key)
    }

    clear(): void {
        this.state.clear()
    }

    // 新增事件总线功能
    on(eventName: string, callback: Function): void {
        if(!this.eventListeners.has(eventName)) {
            this.eventListeners.set(eventName, [])
        }
        this.eventListeners.get(eventName)?.push(callback)
    }
    
    emit(eventName: string, data?: any): void {
        const listeners = this.eventListeners.get(eventName)
        if(listeners) {
            listeners.forEach(callback => {
                try {
                    callback(data)
                } catch (error) {
                    console.error('事件回调执行错误:', error)
                }
            })
        }
    }

    off(eventName: string, callback?: Function): void {
        if (!callback) {
            this.eventListeners.delete(eventName)
        } else {
            const listeners = this.eventListeners.get(eventName)
            if (listeners) {
                const index = listeners.indexOf(callback)
                if (index > -1) {
                    listeners.splice(index, 1)
                }
            }
        }
    }

}

export const globalState = GlobalState.getInstance()
使用
  1. 组件A -- a.vue  【当引擎实例化成功后,设置引擎数据,并触发广播,在适合的时机销毁相关数据】
// a.vue 当引擎实例化成功后,设置引擎数据,并触发广播
import { globalState } from "@/stores/globalState";
const InstanceHasInited = (data) => {
      if (engine) {
            globalState.set("engineInstance", data);
            // 触发事件,通知其他组件
            globalState.emit("engineInstance:created", data);
      }
});
// 销毁相关数据
onUnmounted(() => {
  globalState.delete("engineInstance");
  globalState.off("engineInstance:created");
});
  1. 组件B -- b.vue  【在需要使用引擎api获取数据的位置添加监听】

import { globalState } from "@/stores/globalState";
const handleInstanceCreated = (engine: Engine) => {
    if(engine) {
        // 调用相关api
    }
}
onMounted(() => {
  // 监听引擎实例创建事件
  globalState.on("engineInstance:created", handleInstanceCreated);
})