This commit is contained in:
王一之 2024-11-14 18:01:54 +08:00
parent eeaf9e071e
commit e9cdb48d30
3 changed files with 80 additions and 67 deletions

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import EventEmitter from "eventemitter3"; import EventEmitter from "eventemitter3";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
@ -23,6 +24,7 @@ export class Server {
}); });
} }
on(eventName: "connection", callback: (con: IConnect) => void): void;
on(eventName: string, callback: (con: IConnect) => void) { on(eventName: string, callback: (con: IConnect) => void) {
this.EE.on(eventName, callback); this.EE.on(eventName, callback);
} }
@ -31,29 +33,11 @@ export class Server {
export class Connect { export class Connect {
private EE: EventEmitter; private EE: EventEmitter;
private con: IConnect; constructor(private con: IConnect) {
constructor(
private id: string | IConnect,
con?: IConnect
) {
this.EE = new EventEmitter(); this.EE = new EventEmitter();
if (arguments.length === 1) { this.con.onMessage((message) => {
this.con = id as IConnect; this.messageHandler(message);
this.con.onMessage((message) => { });
this.messageHandler(message);
});
} else {
// 子连接
this.con = con!;
this.con.onMessage((message) => {
const data = message as { eventName: string; data: unknown; id: string };
if (data.eventName === "subcon") {
if (data.id !== this.id) return;
this.messageHandler(data.data);
}
});
}
this.con.onDisconnect(() => { this.con.onDisconnect(() => {
this.EE.emit("disconnect"); this.EE.emit("disconnect");
this.EE.removeAllListeners(); this.EE.removeAllListeners();
@ -67,12 +51,16 @@ export class Connect {
} }
private messageHandler(data: unknown) { private messageHandler(data: unknown) {
const subData = data as { eventName: string; data: unknown[]; messageId: string }; const subData = data as { eventName: string; data: unknown[]; messageId: string; conType: string; id: string };
if (subData.eventName === "callback") {
this.EE.emit(subData.eventName + subData.messageId, ...subData.data);
return;
}
subData.data.push(this.callbackFunc(subData.messageId)); subData.data.push(this.callbackFunc(subData.messageId));
this.EE.emit(subData.eventName, ...subData.data); this.EE.emit(subData.eventName, ...subData.data);
} }
on(eventName: string, callback: (message: unknown) => void) { on(eventName: string, callback: (...args: any[]) => void) {
this.EE.on(eventName, callback); this.EE.on(eventName, callback);
} }
@ -80,17 +68,18 @@ export class Connect {
this.con.postMessage({ eventName, data }); this.con.postMessage({ eventName, data });
} }
emit(eventName: string, ...data: unknown[]) { emit(eventName: string, ...data: any[]) {
// 判断最后一个参数是否为函数 // 判断最后一个参数是否为函数
const callback = data.pop(); const callback = data.pop();
const messageId = uuidv4();
if (typeof callback !== "function") { if (typeof callback !== "function") {
data.push(callback); data.push(callback);
} else {
this.EE.on("callback" + messageId, (...args) => {
callback(...args);
});
} }
this.con.postMessage({ eventName, data, messageId: uuidv4() }); const sendData = { eventName, data, messageId };
} this.con.postMessage(sendData);
// 子连接
connect() {
return new Connect(uuidv4(), this.con);
} }
} }

View File

@ -1,42 +1,65 @@
// @vitest-environment jsdom // @vitest-environment jsdom
import { expect, test, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { Server, Connect } from "."; import { Server, Connect } from ".";
import { connect, WindowServer } from "./window"; import { connect, WindowServer } from "./window";
test("server", async () => { describe("server", () => {
const myFunc = vi.fn(); it("hello", async () => {
const server = new Server(new WindowServer(global.window)); const myFunc = vi.fn();
server.on("connection", (con) => { const server = new Server(new WindowServer(global.window));
myFunc(); server.on("connection", (con) => {
con.onMessage((message) => { myFunc();
myFunc(message); con.onMessage((message) => {
myFunc(message);
});
}); });
const client = connect(window, window);
client.postMessage("hello");
await new Promise((resolve) => setTimeout(resolve, 10));
expect(myFunc).toHaveBeenCalledTimes(2);
expect(myFunc).toHaveBeenCalledWith("hello");
}); });
const client = connect(window, window);
client.postMessage("hello");
await new Promise((resolve) => setTimeout(resolve, 1));
expect(myFunc).toHaveBeenCalledTimes(2);
expect(myFunc).toHaveBeenCalledWith("hello");
}); });
test("connect", async () => { describe("connect", async () => {
const myFunc = vi.fn(); it("hello", async () => {
const server = new Server(new WindowServer(global.window)); const server = new Server(new WindowServer(global.window));
server.on("connection", (con) => { const myFunc = vi.fn();
myFunc(); server.on("connection", (con) => {
const wrapCon = new Connect(con); myFunc();
wrapCon.on("hello", (message) => { const wrapCon = new Connect(con);
myFunc(message); wrapCon.on("hello", (message) => {
wrapCon.emit("world", "world"); myFunc(message);
wrapCon.emit("world", "world");
});
}); });
const client = new Connect(connect(window, window));
client.on("world", (message) => {
myFunc(message);
});
client.emit("hello", "hello");
await new Promise((resolve) => setTimeout(resolve, 10));
expect(myFunc).toHaveBeenCalledTimes(3);
expect(myFunc).toHaveBeenCalledWith("hello");
expect(myFunc).toHaveBeenCalledWith("world");
}); });
const client = new Connect(connect(window, window)); it("response", async () => {
client.on("world", (message) => { const server = new Server(new WindowServer(global.window));
myFunc(message); const myFunc = vi.fn();
server.on("connection", (con) => {
const wrapCon = new Connect(con);
wrapCon.on("ping", (message, response) => {
myFunc(message);
response("pong");
});
});
const client = new Connect(connect(window, window));
client.emit("ping", "ping", (message: string) => {
myFunc(message);
});
await new Promise((resolve) => setTimeout(resolve, 10));
expect(myFunc).toHaveBeenCalledTimes(2);
expect(myFunc).toHaveBeenCalledWith("ping");
expect(myFunc).toHaveBeenCalledWith("pong");
}); });
client.emit("hello", "hello");
await new Promise((resolve) => setTimeout(resolve, 1));
expect(myFunc).toHaveBeenCalledTimes(3);
expect(myFunc).toHaveBeenCalledWith("hello");
expect(myFunc).toHaveBeenCalledWith("world");
}); });

View File

@ -20,8 +20,9 @@ export class WindowServer implements IServer {
} }
export function connect(source: Window, target: Window) { export function connect(source: Window, target: Window) {
const con = new WindowConnect(uuidv4(), source, target); const connectId = uuidv4();
con.postMessage({ type: "connect" }); target.postMessage({ type: "connect", connectId }, "*");
const con = new WindowConnect(connectId, source, target);
return con; return con;
} }
@ -35,14 +36,14 @@ export class WindowConnect implements IConnect {
) { ) {
this.EE = new EventEmitter(); this.EE = new EventEmitter();
this.source.addEventListener("message", (event) => { this.source.addEventListener("message", (event) => {
if (event.data.id === id) { if (event.data.eventName === "message" && event.data.id === id) {
this.EE.emit("message", event.data); this.EE.emit("message", event.data.data);
} }
}); });
} }
postMessage(message: unknown) { postMessage(data: unknown) {
this.target.postMessage(message, "*"); this.target.postMessage({ eventName: "message", id: this.id, data }, "*");
} }
onMessage(callback: (message: unknown) => void) { onMessage(callback: (message: unknown) => void) {