- WebSocket是一种在
Web
应用程序中提供双向通信
的协议。它允许服务器和客户端之间建立持久
的连接,实现实时数据传输。相比传统的HTTP请求,WebSocket具有更低的延迟
和更高的效率
。 - WebSocket使用了一种新的
特殊的
协议头,在客户端和服务器之间进行握手,建立连接后,可以通过发送消息进行双向通信。这使得服务器能够主动向客户端推送数据,而不需要客户端发起请求。 - WebSocket可以在浏览器中使用,也可以在服务器端使用。在浏览器中,你可以使用JavaScript的 WebSocket API 来创建WebSocket连接,并处理接收和发送的消息。在服务器端,你可以使用相应的编程语言库或框架提供的WebSocket实现来创建和管理WebSocket连接。
- WebSocket在很多应用场景中都非常有用,比如实时聊天应用、实时数据更新、多人协作和实时游戏等。它提供了一种
高效
、可靠
和实时
的通信方式,已经成为现代Web开发中常用的技术之一。
本文主要实现web端
websocket
封装及创建,框架以vue3
、pinia
、typescript
项目为例
实现
stores
extractStore.ts
import type {
PiniaCustomStateProperties,
StoreActions,
StoreGeneric,
StoreGetters,
StoreState,
} from "pinia";
import type { ToRefs } from "vue";
import { isReactive, isRef, toRaw, toRef } from "vue";
type Extracted<SS> = ToRefs<
StoreState<SS> & StoreGetters<SS> & PiniaCustomStateProperties<StoreState<SS>>
> &
StoreActions<SS>;
export function extractStore<SS extends StoreGeneric>(
store: SS,
): Extracted<SS> {
const rawStore = toRaw(store);
const refs: Record<string, unknown> = {};
for (const [key, value] of Object.entries(rawStore)) {
if (isRef(value) || isReactive(value)) {
refs[key] = toRef(store, key);
} else if (typeof value === "function") {
refs[key] = value;
}
}
return refs as Extracted<SS>;
}
permission
state.ts
import { defineStore } from "pinia";
import WS from "@/websocket";
interface State {
ws: InstanceType<typeof WS> | null;
message: any;
}
export const usePermissionState = defineStore({
id: "permission.state",
state: (): State => {
return {
ws: null,
message: "",
};
},
});
getters.ts
import { computed } from "vue";
import { defineStore } from "pinia";
import { usePermissionState } from "./state";
export const usePermissionGetters = defineStore("permission.getters", () => {
const state = usePermissionState();
return {
message: computed((): any => state.message),
};
});
actions.ts
import { defineStore } from "pinia";
import { usePermissionState } from "./state";
import WS from "@/websocket";
export const usePermissionActions = defineStore("permission.actions", () => {
const state = usePermissionState();
const createWebsocket = async (): Promise<void> => {
state.ws = new WS();
state.ws.connection();
};
const subscribeWebsocket = (msg: any): void => {
state.ws?.subscribe(msg);
};
const getWebsocketMsg = (payload: any): void => {
state.message = payload;
};
const setWebsocketMsg = async (payload: any): Promise<ReturnType<typeof getWebsocketMsg>> => {
return getWebsocketMsg(payload);
};
return {
createWebsocket,
subscribeWebsocket,
setWebsocketMsg,
getWebsocketMsg
};
});
index.ts
import { extractStore } from "@/stores/extractStore";
import { defineStore } from "pinia";
import { usePermissionActions } from "./actions";
import { usePermissionGetters } from "./getters";
import { usePermissionState } from "./state";
export const usePermissionStore = defineStore("permission", () => {
return {
...extractStore(usePermissionActions()),
...extractStore(usePermissionGetters()),
...extractStore(usePermissionState()),
};
});
websocket
websocket-type.ts
export declare interface IWS {
connection(): void;
subscribe: (msg: any) => void;
}
export declare interface IConfig {
url?: string;
port?: number;
protocol?: string;
time?: number;
}
export declare type GetLocal = (key: string) => string;
index.ts
import type { IWS, IConfig, GetLocal } from "./websocket-type";
import { usePermissionStore } from "@/stores/permission";
const permissionStore = usePermissionStore();
const getLocal: GetLocal = (key) => {
let value = localStorage.getItem(key) || "";
if (value.startsWith("[") || value.includes("{")) {
return JSON.parse(value);
} else {
return value;
}
};
class WS implements IWS {
private url: string;
private port: number;
private protocol: string;
private time: number;
private ws: null | WebSocket = null;
private timer: null | ReturnType<typeof setTimeout> = null;
constructor(config: IConfig = {}) {
this.url = config.url || "localhost"; //路径
this.port = config.port || 4000; //端口号
this.protocol = config.protocol || "ws"; //协议
this.time = config.time || 30 * 1000; //心跳检测时间
}
connection() {
this.ws = new WebSocket(`${this.protocol}://${this.url}:${this.port}`);
this.ws.onopen = this.onOpen; //连接成功后事件
this.ws.onmessage = this.onMessage; //收到消息后事件
this.ws.onclose = this.onClose; //连接关闭事件
this.ws.onerror = this.onError; //连接异常事件
}
private onOpen = () => {
//发送消息
this.ws?.send(
JSON.stringify({
//鉴权
type: "auth",
data: getLocal("token"),
}),
);
};
private onMessage = (e: MessageEvent): void => {
let { type, data } = JSON.parse(e.data);
switch (type) {
case "noAuth":
console.log("没有权限");
break;
case "heartCheck": //心跳检测
this.checkServer();
this.ws?.send(
JSON.stringify({
type: "heartCheck",
msg: "yes",
}),
);
break;
default:
permissionStore.setWebsocketMsg(data); // 发送数据
}
};
private onClose = (): void => {
this.ws?.close();
};
private onError = (): void => {
setTimeout(() => {
this.create();
}, 1000);
};
subscribe = (msg: any) => {
this.ws?.send(JSON.stringify(msg));
};
private checkServer(): void {
this.timer && clearTimeout(this.timer); //防抖
this.timer = setTimeout(() => {
this.onClose();
this.onError();
}, this.time + 1000); //断线重联
}
}
export default WS;