websocket是HTML5開始提供的一種在單個TCP連線上進行全雙工通訊的協議。大多數 Web 應用程式將通過頻繁的非同步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源。而websocket能夠很好的解決類似的問題。常用於即時通訊、監控等情況。 在本案例中, 使用egg+typescript作為後端框架, 使用egg官方封裝的Socket.IO庫。前端使用vue。
egg配置
TS下的egg許多設定和文件有所不同, 但是稍稍看一下還是能理解的。 按照egg的檔案規劃,Socket.IO應該獨立的作為一個資料夾, 內部包含自己的controller和middleware, 然後和其他頁面共享router配置
// router
export default (app: Application) => {
const { controller, router, io } = app;
// Socket.IO會通過io暴露出來, io.of('/')則是名稱空間
router.get('/', controller.home.index); // 這個是正常的http請求
io.of('/').route('online', io.controller.nsp.exchange); // 這個就是websocket請求
io.of('/').route('newmsg', io.controller.nsp.newmsg);
io.of('/').route('sendMsg', io.controller.nsp.sendMsg);
};
複製程式碼
// config/plugin 這裡是egg開啟外掛外掛的地方
const plugin: EggPlugin = {
// static: true,
nunjucks: {
enable: true,
package: 'egg-view-nunjucks', // 模板渲染外掛
},
io: {
enable: true,
package: 'egg-socket.io', // 官方的封裝的socke外掛
},
};
複製程式碼
// config/config.default 根據不同環境, 配置外掛引數
export default (appInfo: EggAppInfo) => {
// ...
config.middleware = [];
// 配置模板引擎
config.view = {
cache: false,
defaultExtension: 'nunjucks',
mapping: {
'.html': 'nunjucks',
},
};
// 配置socket
config.io = {
init: { }, // passed to engine.io
namespace: {
// 名稱空間
'/': {
connectionMiddleware: [
'connection', // 這個是連線中介軟體, 只在connection的時候觸發
],
packetMiddleware: [], // 這個會在每次訊息的時候觸發
},
'/example': {
connectionMiddleware: [],
packetMiddleware: [],
},
},
};
return config;
};
複製程式碼
// app/router
export default (app: Application) => {
const { controller, router, io } = app;
router.get('/', controller.home.index);
// 這裡的sendMsg相當於一個介面, 負責處理客戶端傳送的sendMsg事件
// 這個controller是io模組的controller, 和egg的controller不同
io.of('/').route('sendMsg', io.controller.nsp.sendMsg);
};
複製程式碼
// app/io/controller
import { Controller } from 'egg';
export default class NspController extends Controller {
public async sendMsg() {
const { ctx, app } = this;
const nsp = app.io.of('/');
const message = ctx.args[0] || {}
// 向客戶端廣播訊息, 在客戶端監聽broadcast事件就可以獲取訊息了
nsp.emit('broadcast', message)
}
}
複製程式碼
// app/io/middleware/connection
import { Context } from 'egg';
// io模組的中介軟體, 在config/config.default裡配置成connectionMiddleware, 只在
connection的時候觸發
export default function robotMiddleware() {
return async (ctx: Context, next: any) => {
const { app } = ctx;
const nsp = app.io.of('/');
// 向客戶端推送online事件
nsp.emit('online', '有新成員加入聊天室了')
await next();
};
}
複製程式碼
這樣, 伺服器端的邏輯就完成了,接下來通過vue來實現客戶端邏輯
簡單的一個頁面, 分為MsgItem和Send 兩個元件, 具體實現就不寫了。// 客戶端 src/utils/io
import io from 'socket.io-client';
// 稍微封裝一下socket.io, 然後暴露出去。
const socket = function ():any {
const _io = io('http://127.0.0.1:7001/');
_io.on('connect', function(){
console.log('連結成功');
});
_io.on('disconnect', function(){
console.log('斷開連級');
});
return _io
}
export default socket
複製程式碼
// 客戶端 app/App.vue
export default class Index extends Vue {
// 頭像
user = {
avatar: 'https://f12.baidu.com/it/u=4263977612,1595937908&fm=76'
}
// 訊息列表
msgList: Array<string> = []
//傳送訊息, 觸發sendMsg事件
sendMsg(msg: string):void {
socket.emit('sendMsg', msg)
}
// 頁面載入之後觸發
mounted() {
// 監聽online事件
socket.on('online', (data: string) => {
this.msgList.push(data)
})
// 監聽broadcast事件, 獲取伺服器訊息
socket.on('broadcast', (data: string) => {
this.msgList.push(data)
})
}
}
複製程式碼
socket是基於事件監聽來進行的, 通過on來註冊監聽事件, 通過emit來觸發事件。 通過伺服器和客戶端的配合, 就可以事件即時的訊息推送。這篇文章只寫了最簡單功能, 基於socket.io還有分房間, 踢人,身份識別,私聊等功能, 等下篇文章再寫吧。