作者:陈畏民
源起
往年暑假的前半段工夫, 在家捣鼓了一个情侣类 web 利用, 基于 aspnetcore 和 angular 搭建的; 暑假中实现了 ’ 告白 ’, ‘ 相册 ’, ‘ 说说 ’, ‘ 纪念日 ’ 这些性能, 而后前端界面上留一个性能的坑位: 聊天 , 点击这个 聊天 按钮, 能够看到四个字, 那就是 敬请期待
; 部署上线后, 用户当然只有我和我的 ” 好敌人 ” 应用, “ 好敌人 ” 先跨了我真棒, 而后问聊天性能马上能够用了吧? 我缄默了, 心想着这个性能前面用 signalr 试试看吧; 当初曾经 2020 秋了, 聊天性能的界面上仍旧是那四个字: 敬请期待
!
这个国庆, 我意识到不能再拖了, 本人埋的坑, 应该趁早把它填了, 否则 ” 好敌人 ” 会感觉你很菜, 一个 ” 简略的 ” 聊天性能都做不进去;
遇见声网(agora)
最开始想用 signalr 本人实现聊天性能的, 然而思考到一方面, 本人的服务器资源无限(1 核 1G 轻量应用服务器); 另一方面, 本身精力能力无限, 写进去兴许不难, 然而要写好确是不简略的; 于是寻思着找找现成的货色用用吧, 机缘巧合, 我据说了声网(agora), 于是去他的官网看了一番, 看到有具体的文档, 足量的收费额度 … 于是决定先白嫖试用一下
对于 agora
顺便找了一下 agora 的相干材料, 看起来是挺靠谱的, 在寰球都有数据中心和服务器; 小米、陌陌、新东方等知名企业都用过他们的云服务;
基于 agora 的 rtm sdk 给我的利用加上聊天性能
参考官网的文档
我的环境
- win10 零碎
- npm 包治理
- angular8.x
- vscode
步骤
装置依赖
npm i agora-rtm-sdk
装置完后须要批改下 agora-rtm-sdk/index.d.ts
的文件的 2258 行
原来的内容为:
export type {RtmChannel, RtmClient, RtmEvents, RtmMessage, RtmStatusCode};
批改为:
export {RtmChannel, RtmClient, RtmEvents, RtmMessage, RtmStatusCode};
不批改的话, 编译会报错
引入依赖
因为是在在 angular 组件 ChatComponent
中实现聊天相干的性能, 所以在其中引入 rtm sdk 的依赖 import AgoraRTM from 'agora-rtm-sdk';
创立 rtm 客户端并登陆到 agora 的 rtm 服务器
一行代码创立 rtm 客户端:
const rtmClient = AgoraRTM.createInstance('<your app id>');
<details>
<summary> 登陆到 rtm 服务器 </summary>
const rtmClient = AgoraRTM.createInstance('fd033b52ca5d40599efc96f6e2131639');
async function rtmClientLogin(user: User) {
try {await rtmClient.login({ token: null, uid: user.id});
} catch(err) {console.log('AgoraRTM client login failure', err);
}
}
// 在组件的 ngOnInit 办法中调用 rtmClientLogin
async ngOnInit() {
try {let user = await this.userServ.getUser().toPromise();
if (user instanceof User) {
this.user = user;
rtmClientLogin(this.user);
} else {throw new Error('无奈获取用户数据');
}
} catch(err) {this.notifyServ.error('初始化聊天组件失败', null);
console.error('初始化聊天组件失败', err);
}
}
</details>
ps: 测试阶段, 所以应用的 rtm 的受权形式是 AppID, 如果要应用这种受权形式, 在 rtm 控制台创立我的项目的时候要留神一下, 身份认证模式勾选 App ID
, 否则在登陆到 rtm 服务器的时候, 会报红
发送 / 接管音讯
音讯发送失败须要告诉用户, 谬误告诉间接应用了 antdesign 的 NzNotificationService
, 在构造函数注入即可; 这个利用中, 相互发消息的单方是情侣, User
示意以后用户, User.Spouse
示意用户的伴侣; 音讯发送胜利须要清空发送音讯文字框并将发送的音讯退出音讯数组中, 让 angular 更新视图
<details>
<summary> 发送音讯 </summary>
async sendMessage() {if (!this.newMessage) {return;}
const spouseId = this.user.spouse.id;
try {
const result = await rtmClient.sendMessageToPeer({text: this.newMessage}, spouseId);
if (!result.hasPeerReceived) {throw new Error('对方未承受音讯');
} else {
this.messages.push({
text: this.newMessage,
sender: this.user,
receiver: this.user.spouse,
dateSended: new Date()});
this.newMessage = undefined;
}
} catch (err) {this.notifyServ.error('发送音讯失败', null);
console.log('发送音讯失败', err);
}
}
</details>
<br/>
在 ngOnInit
生命周期函数中监听收到新音讯事件, 收到新音讯后, 将新音讯退出音讯数组中, angular 会通过数据绑定更新视图, 渲染 ui
<details>
<summary> 监听并解决收到新音讯事件 </summary>
async ngOnInit() {
try {let user = await this.userServ.getUser().toPromise();
if (user instanceof User) {
this.user = user;
rtmClientLogin(this.user);
监听接管到音讯事件
rtmClient.on('MessageFromPeer', (rtmMessage, peerId) => {
this.messages.push({
text: rtmMessage.text,
sender: this.user.spouse,
receiver: this.user,
dateSended: new Date()});
});
} else {throw new Error('无奈获取用户数据');
}
} catch(err) {this.notifyServ.error('初始化聊天组件失败', null);
console.error('初始化聊天组件失败', err);
}
}
</details>
<details>
<summary> 前端 html 代码 </summary>
<div id="container">
<div class="messages">
<div class="message-item"
*ngFor="let msg of messages">
<div class="sendedMessage"
*ngIf="msg.sender.id === user.id">
<span class="message-text">{{msg.text}}</span>
<span>
<nz-avatar nzIcon="user"
[nzSrc]="msg.receiver.profileImageUrl"></nz-avatar>
</span>
</div>
<div class="receviedMessage"
*ngIf="msg.sender.id === user.spouse.id">
<span>
<nz-avatar nzIcon="user"
[nzSrc]="msg.sender.profileImageUrl"></nz-avatar>
</span>
<span class="message-text">{{msg.text}}</span>
</div>
</div>
</div>
<div class="new-message">
<div nz-row
nzJustify="end">
<div nz-col
nzSpan="18">
<textarea nz-input
[(ngModel)]="newMessage"
[nzAutosize]="{minRows: 1, maxRows: 6}"></textarea>
</div>
<div nz-col
nzSpan="6">
<button nz-button
nzType="primary"
class="mx-auto"
style="width: 100%;"
(click)="sendMessage()"> 发送 </button>
</div>
</div>
</div>
</div>
</details>
<details>
<summary> 前端 css</summary>
:host {
height: 100%;
display: block;
position: relative;
}
#container {
height: 100%;
display: flex;
flex-direction: column;
padding-top: 4px;
}
.messages {flex-grow: 1;}
nz-alert {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 85%;
text-align: center;
}
.sendedMessage {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 8px;
}
.receviedMessage {margin-bottom: 8px;}
.message-text {
background: #fff;
padding: 8px 4px;
}
.sendedMessage .message-text {margin-right: 4px;}
.receviedMessage .message-text {margin-left: 4px;}
</details>
成果如何?
动图演示:
静图:
小结
上文基于 agora 的 rtm sdk, 初步实现了简略的聊天性能; 体验下来感觉很不便, 不须要关注后端实现, 只须要解决前端逻辑即可轻松构建出实时聊天性能; 当然, 正式在生产环境应用, 还是须要后端配合生成一个身份认证令牌 (token) 来保障安全性的; 上文临时只实现了文字的发送接管, 实际上 rtm sdk 还反对文件和图片的收发, 性能很弱小, 有机会再持续摸索。
更多实时互动相干内容,可点击进入声网 RTC 开发者社区进行查看