useVModel
组件绑定一个v-model属性。 注意:在Vue3.3版本中,简化了用法,支持 defineModel 直接声明,自动注册一个 prop,并返回一个可以直接变更的 ref
源码
/**
* @description 组件绑定一个v-model
* @param {Object} props 组件的props
* @param {propName} props 属性名称
* @param {Function} props Vue的emit方法
* @date
* @author
*/
import { computed } from 'vue';
export function useVModel(props: object, propName: string, emit) {
return computed({
get: () => {
if (typeof props[propName] === 'object' && props[propName] !== null) {
const obj = {};
return new Proxy(props[propName], {
set(target, name, val) {
let value
if(Array.isArray(target)){
// 当为数组时,name为数组的索引
value = target
value[name] = val
} else {
obj[name] = val; // vue更新原理只更新一次,所以当更改多个值时只会有一个值更新的bug,所以通过obj收集修改的属性,emit统一处理
value = {
...target,
...obj,
[name]: val,
}
}
emit('update:' + propName, value);
return true;
},
});
} else {
return props[propName];
}
},
set: (val) => {
emit('update:' + propName, val);
},
});
}
示例
定义一个组件Child,使用useVModel双向绑定
<script setup lang="ts">
import { useVModel } from "m-ui/hooks";
const props = defineProps({
model: {
type: Object,
default: () => ({
input: null,
select: null,
}),
},
});
const emit = defineEmits(["update:model"]);
const _model = useVModel(props, "model", emit);
</script>
<template>
<ms-space>
<ms-input v-model:value="_model.input" />
<ms-select
class="w-[200px]"
v-model:value="_model.select"
:options="[{ value: 'select', label: '下拉框' }]"
/>
</ms-space>
</template>
使用时通过v-model双向绑定
<script setup lang="ts">
import Child from "./Child.vue";
const model = ref({
input: "你好",
select: null,
});
const handleClick = () => {
console.log(model.value);
};
</script>
<template>
<ms-card>
<Child v-model:model="model" />
</ms-card>
<ms-button class="mt-4" type="primary" @click="handleClick">
打印model
</ms-button>
</template>
useChannel
多窗口/标签页通信, 创建一个频道,广播通知。
源码
/**
* useChannel-- 多窗口/标签页通信, 创建一个频道,广播通知
* @param {String} name 频道key
* @param {Function} onMessage (data) => void 接收消息后的回调
* @return {Object} { channel: BroadcastChannel; postMessage: (message: any) => void }
* @return {Object.channel} channel 实例对象
* @return {Object.postMessage} 广播消息的方法
* @return {Object.closeChannel} 关闭断开与频道的连接
* @author bingao.liu
*/
export function useChannel(
name: string,
onMessage: (data: any) => void
): { channel: BroadcastChannel; postMessage: (message: any) => void; closeChannel: () => void } {
// BroadcastChannel是一种Web API,允许在同一来源(同一协议、主机名和端口号)的不同浏览器上下文
//(如标签页、iframe、Web Worker)之间进行消息传递。
// 它提供了一种简便、可靠的方法来实现跨上下文的实时通信
const channel = new BroadcastChannel(name);
// 监听频道 message 事件,频道收到消息时触发 message 事件
const handleMessage = (e: MessageEvent<any>) => {
try {
onMessage?.(e.data);
} catch (error) {
console.error(error);
}
};
channel.addEventListener('message', handleMessage);
// 关闭断开与频道的连接
const closeChannel = () => {
try {
channel.removeEventListener('message', handleMessage);
channel.close();
} catch (error) {
console.error(error);
}
};
window.addEventListener('beforeunload', closeChannel);
// 发送消息
const postMessage = (message: any) => {
try {
channel.postMessage(message);
} catch (error) {
console.error(error);
}
}
return {
channel,
postMessage,
closeChannel,
};
}
useWebSocket
建立一个WebSocket长连接
源码
/**
* @description useWebSocket--创建一个WebSocket连接
* @param {String} url 连接的url
* @param {Function} onMessage (data) => void 接收消息后的回调
* @date
* @author
*/
export function useWebSocket(
url: string,
onMessage: Function
): { initWebSocket: () => void } {
function initWebSocket() {
const ws = new WebSocket(`ws://${url}`);
ws.onopen = function () {
console.log("socket连接成功");
};
// 监听socket错误信息
ws.onerror = function () {
console.log("连接错误");
};
ws.onmessage = function (msg) {
console.log(msg.data);
onMessage && onMessage(msg);
};
ws.onclose = function () {
console.log("socket已经关闭");
setTimeout(reconnectWebSocket, 3000);
};
}
initWebSocket();
//重新连接webSocket
function reconnectWebSocket() {
initWebSocket(); //重新连接
}
return { initWebSocket };
}
useScheduler
接口调度,控制请求并发数。
/**
* @description useScheduler--接口调度,控制请求并发数
* @param {Number} count 最大并发数
* @date 12/01/2023
* @author
*/
export function useScheduler(count: number) {
return new Scheduler(count);
}
class Scheduler {
count: number;
queue: Array<any>;
run: Array<any>;
constructor(count: number) {
this.count = count;
this.queue = [];
this.run = [];
}
add(task: any) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
if (this.run.length < this.count) {
this._runTask();
}
});
}
_runTask() {
const { task, resolve, reject } = this.queue.shift();
this.run.push(task);
task()
.then(
(result: any) => {
resolve(result);
},
(error: any) => {
reject(error);
}
)
.finally(() => {
this.run.splice(this.run.indexOf(task), 1);
if (this.queue.length > 0) {
this._runTask();
}
});
}
}