npm init midway@latest -y然后在项目中安装WebSocket的依赖包:
npm i @midwayjs/ws@3 --save npm i @types/ws --save-dev在/src/configuration.ts中开启WebSocket组件:
// 堆代码 duidaima.com // src/configuration.ts import { Configuration } from '@midwayjs/core'; import * as ws from '@midwayjs/ws'; @Configuration({ imports: [ws], // ... }) export class MainConfiguration { async onReady() { // ... } }然后我们开始写接口,首先在项目目录创建socket目录:
├── package.json ├── src │ ├── configuration.ts ## 入口配置文件 │ ├── interface.ts │ └── socket ## ws 服务的文件 │ └── hello.controller.ts ├── test ├── bootstrap.js ## 服务启动入口 └── tsconfig.json通过 @WSController 装饰器定义 WebSocket 服务:
import { WSController } from '@midwayjs/core'; @WSController() export class HelloSocketController { // ... }当有客户端连接时,会触发 connection 事件,我们在代码中可以使用 @OnWSConnection() 装饰器来修饰一个方法,当每个客户端第一次连接服务时,将自动调用该方法。
import { WSController, OnWSConnection, Inject } from '@midwayjs/core'; import { Context } from '@midwayjs/ws'; import * as http from 'http'; // 堆代码 duidaima.com @WSController() export class HelloSocketController { @Inject() ctx: Context; @OnWSConnection() async onConnectionMethod(socket: Context, request: http.IncomingMessage) { console.log(`namespace / got a connection ${this.ctx.readyState}`); } }WebSocket 是通过事件的监听方式来获取数据。Midway 提供了 @OnWSMessage() 装饰器来格式化接收到的事件,每次客户端发送事件,被修饰的方法都将被执行。
import { WSController, OnWSMessage, Inject } from '@midwayjs/core'; import { Context } from '@midwayjs/ws'; @WSController() export class HelloSocketController { @Inject() ctx: Context; @OnWSMessage('message') async gotMessage(data) { return { name: 'harry', result: parseInt(data) + 5 }; } }我们可以通过 @WSBroadCast 装饰器将消息发送到所有连接的客户端上。
import { WSController, OnWSConnection, Inject } from '@midwayjs/core'; import { Context } from '@midwayjs/ws'; @WSController() export class HelloSocketController { @Inject() ctx: Context; @OnWSMessage('message') @WSBroadCast() async gotMyMessage(data) { return { name: 'harry', result: parseInt(data) + 5 }; } @OnWSDisConnection() async disconnect(id: number) { console.log('disconnect ' + id); } }至此,基础的WebSocket接口就开发好了。
/src/config/config.default.ts // src/config/config.default export default { // ... webSocket: { port: 3000, }, }接下来我们前端来调用测试下。
useEffect(async () => { const ws = new WebSocket(`ws://localhost:9999`); ws.onopen = () => { console.log('连接成功'); ws.send(1); }; ws.onmessage = (e) => { console.log('服务端响应:', e); }; ws.onclose = (e) => { console.log('关闭连接,服务端响应:', e) } }, []);这样我们在前端就可以打印出服务端返回的结果,同时服务端获取到了日志信息,前后端的WebSocket链路打通了。
后端:
import { WSController, Inject, OnWSConnection, OnWSMessage, OnWSDisConnection, } from '@midwayjs/core'; import { Context } from '@midwayjs/ws'; import * as http from 'http'; const orderInfo = { status: 'pending', id: 1, // ...orderInfo }; @WSController() export class HelloSocketController { @Inject() ctx: Context; // 客户端第一次连接 @OnWSConnection() async onConnectionMethod(socket: Context, request: http.IncomingMessage) { this.ctx.logger.info(`namespace / got a connection ${this.ctx.readyState}`); setTimeout(() => { orderInfo['status'] = 'success'; }, 3000); } // 收到客户端消息 @OnWSMessage('message') async gotMessage() { if (orderInfo.status === 'success') { return { result: true }; } return { result: false }; } // 客户端断开连接 @OnWSDisConnection() async disconnect(id: number) { console.log('disconnect ' + id); } }.在后端mock一个orderInfo;
.三秒后响应给前端新状态;
useEffect(async () => { const ws = new WebSocket(`ws://localhost:9999`); ws.onopen = () => { console.log('连接成功'); timer.current = setInterval(() => { ws.send(1); }, 1000); }; ws.onmessage = (e) => { console.log('服务端响应:', e); if (JSON.parse(e.data).result) { setOrderPass(true); } }; ws.onclose = (e) => { console.log('关闭连接,服务端响应:', e); }; }, []); return <>订单状态:{orderPass ? '通过' : '申请中'}</>;改造后,前端页面刷新,三秒后订单状态异步更改为通过,达到了页面监听实时刷新的效果,这样的效果是不是比传统Http定时轮询性能更优呢?