1. 状态管理初始化
- 导入Vue3的响应式API
onMounted
和ref
。 - 定义了五个
ref
变量,分别代表缓存仓库(cacheStore
)、本地排名(ranking
)、延迟队列(delayMsg
)、用户签到打卡记录(sign
)以及限流计数器(limiting
)。
2. 缓存管理
putCache
方法用于将属性对象存储到缓存中。takeCache
方法根据key从缓存中获取对应的属性值,同时利用类型断言返回指定类型的数据。lock
、getLock
和unLock
方法实现了对特定资源的加锁、查询锁状态和解锁操作,通过检查缓存中是否存在对应key来判断资源是否被锁定。
3. 消息延迟发送
putDelayMsg
方法将带有过期时间戳的消息放入延迟队列中,每条消息具有唯一标识tag
。- 使用定时器轮询检查延迟队列中消息是否过期,过期后通过事件总线
emitter
发送该消息并移除已处理的消息。
4. 用户签到打卡系统
punchSetting
方法设置用户的签到打卡数据。startSign
方法处理用户签到逻辑,更新签到状态为已签到。continuousClocking
方法计算用户连续签到的天数。
5. 排行榜功能
putRanking
方法向排行榜添加或更新项及其得分。takeRanking
方法按得分排序并获取前total
个排名项,支持降序或升序排列。
6. 辅助工具函数
limit
方法用于限流控制,增加指定键的请求次数计数。takeLimit
方法获取当前键的请求次数。delLimit
方法清除指定键的请求次数记录。getUUID
方法生成全局唯一的UUID字符串。
7. 组件生命周期钩子
- 在
onMounted
钩子中初始化所有ref
变量为新的Map
实例,并设置定时器清理过期缓存及发送过期消息。
通过这个代码示例,我们可以深入理解如何在Vue.js应用中组织复杂的状态管理逻辑,并实现多个相互关联的功能模块。
8. 具体代码
import { onMounted, ref } from "vue";
import emitter from '@/utils/mitt'
interface Prop {
exp: number;
key: string;
data: any;
tag?: any;
}
export default function(){
// 缓存的仓库
let cacheStore = ref( {} as Map< string, Prop >);
// 用于本地排名
let ranking = ref( {} as Map< string, any[] > );
// 延迟队列
let delayMsg = ref( {} as Map< string, any[] > );
// 用户签到打卡
let sign = ref( {} as Map< string, any[] > );
// 限流
let limiting = ref( {} as Map< string, number> );
function limit( key : string ) {
if (limiting.value.has(key)) {
let temp = limiting.value.get(key) as number;
limiting.value.set(key,temp + 1);
}else {
limiting.value.set(key,1);
}
}
function takeLimit( key : string ) : number {
return limiting.value.get(key) as number;
}
function delLimit( key : string ) {
limiting.value.delete(key);
}
// 放入打卡的数据
function punchSetting( key : string , signInData : number[]) {
sign.value.set(key,signInData);
}
// 打卡
function startSign( key : string , day : number ): boolean {
let signInData = sign.value.get(key) as any[];
if (signInData) {
signInData.splice(day - 1,1,1);
return true;
}
return false;
}
// 返回连续打卡的天数
function continuousClocking( key : string ) : number {
if(sign.value.size > 0) {
let signInData = sign.value.get(key) as any[];
if (signInData) {
let day = 1;
let continuous : any[] = [];
signInData.forEach( (value, index) => {
if(signInData[index] === signInData[index+1] && value ==1) {
day++;
}else {
continuous.push(day);
day = 1;
}
});
return Math.max.apply(null,continuous);
}
}
return 0;
}
function putDelayMsg( prop : Prop ) {
prop.tag = getUUID();
if (delayMsg.value.has(prop.key)) {
let delayMsgs = delayMsg.value.get(prop.key) as any[];
delayMsgs.push(prop);
delayMsg.value.set(prop.key,delayMsgs);
}else {
let temp = [];
temp.push(prop);
delayMsg.value.set(prop.key,temp);
}
}
onMounted(() => {
cacheStore.value = new Map();
ranking.value = new Map();
delayMsg.value = new Map();
sign.value = new Map();
// 轮询清除过期的缓存
setInterval(() => {
if(cacheStore.value.size > 0) {
Array.from(cacheStore.value.keys()).forEach( key => {
let data = cacheStore.value.get(key);
if (data) {
let nowDate = new Date().valueOf();
if (data.exp < nowDate && data.exp != -1) {
cacheStore.value.delete(key);
}
}
});
}
},100);
// 发送过期消息让Mitt进行通知 emitter.on('xxx', e => { console.log(e) } )
setInterval(() => {
if(delayMsg.value.size > 0) {
Array.from(delayMsg.value.keys()).forEach( key => {
let temp = delayMsg.value.get(key) as any[];
temp.forEach( item => {
let nowDate = new Date().valueOf();
if( item.exp < nowDate - 100 ) {
emitter.emit(item.key, item.data);
temp = temp.filter( dispose => dispose .tag != item.tag);
delayMsg.value.set(item.key,temp);
}
});
})
}
},100);
});
function putRanking( key : string , data : any) {
if(ranking.value.has(key)) {
let rank = ranking.value.get(key) as any[];
let temp = rank.find(item => item.data === data);
if(temp) {
let newVal = rank.filter(item => item.data !== data );
newVal.push({ data: data.toString() , score : temp.score + 1 });
ranking.value.set(key,newVal);
}else {
rank.push({ data: data.toString() , score : 1 });
ranking.value.set(key,rank);
}
}else {
let temp = [] as any[];
temp.push({ data: data.toString() , score: 1 });
ranking.value.set(key,temp);
}
}
function takeRanking( key : string , total : number , desc : boolean): any[] {
let rank = ranking.value.get(key) as any[];
if(desc) {
rank.sort( ( a , b ) => b.score - a.score );
}else {
rank.sort( ( a , b ) => a.score - b.score );
}
return rank.slice(0, total != 1 ? total - 1 : total);
}
// 放入缓存
function putCache( prop : Prop ) {
cacheStore.value.set(prop.key,prop);
}
// 获取缓存
function takeCache<T>( key : string ) : any {
return cacheStore.value.get(key) as T;
}
// 加锁
function lock( prop : Prop ) : boolean {
if (getLock(prop.key)) {
return false;
}
cacheStore.value.set(prop.key,prop);
return true;
}
// 获取锁
function getLock( key : string ) : boolean {
return cacheStore.value.has(key);
}
// 解锁
function unLock( key : string , id : any) {
let lock = cacheStore.value.get(key);
if( lock ) {
if( lock.data == id ) {
cacheStore.value.delete(key);
}
}
}
// 全局id
function getUUID () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0
const v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
});
}
return { takeCache , putCache , unLock , lock , getLock , putRanking , takeRanking , getUUID , putDelayMsg , startSign , punchSetting , continuousClocking }
}
下面将针对每个主要功能模块提供使用场景及调用方式:
1. 缓存管理
// 创建一个Prop对象实例
const propExample = {
exp: Date.now() + 60 * 1000, // 过期时间为1分钟后
key: 'exampleKey',
data: { someData: 'exampleValue' },
};
// 将数据放入缓存
putCache(propExample);
// 从缓存获取数据
const cacheData = takeCache<typeof propExample>('exampleKey');
console.log(cacheData.data.someData); // 输出:exampleValue
2. 消息延迟发送
// 创建一个带有过期时间的Prop对象
const delayMsgExample = {
exp: Date.now() + 5000, // 5秒后过期
key: 'delayEvent',
data: { message: 'Hello, delayed message!' },
};
// 将消息放入延迟队列
putDelayMsg(delayMsgExample);
// 假设在事件总线emitter中有监听'delayEvent'的处理函数
// emitter.on('delayEvent', (data) => console.log(data.message));
3. 用户签到打卡
// 设置用户的签到数据(假设一个月30天)
const signInData = new Array(30).fill(0);
punchSetting('user1', signInData);
// 用户进行签到
const isSigned = startSign('user1', 5); // 第5天签到
console.log(isSigned); // 输出:true
// 获取连续签到天数
const continuousDays = continuousClocking('user1');
console.log(continuousDays); // 输出:根据实际情况计算的连续签到天数
4. 排行榜功能
// 添加或更新排行榜项
putRanking('gameScore', 'playerA', 100);
putRanking('gameScore', 'playerB', 200);
putRanking('gameScore', 'playerC', 150);
// 获取前3名,按降序排列
const topThree = takeRanking('gameScore', 3, true);
console.log(topThree);