📌 声明:本篇文章基于 Easemob Chat CallKit Vue3 开源项目由 AI 辅助生成。
如果你正在 Vue3 项目中集成音视频通话功能,却被信令协议、状态管理、多端同步、UI 布局搞得焦头烂额——Easemob Chat CallKit Vue3 可能是你一直在找的答案。基于环信 IM + 声网 RTC,内置完整的呼叫、接听、挂断、群聊网格、媒体控制能力,真正的开箱即用。
😫 我们先聊聊:自建音视频通话,到底难在哪?
做过实时音视频的同学应该都有体会,这玩意儿看起来只是"接个 SDK",真动手才发现坑一个接一个:
1. 信令层 = 自己造轮子
IM 消息和 RTC 是完全两套系统。呼叫、接听、拒绝、挂断、超时、占线……每一个动作都要自己定义信令格式、处理发送失败、重连补偿、离线消息过滤。稍不留神,两端状态就对不上——我这边显示"通话中",对方那边已经挂了。
2. 状态管理是灾难
单聊还勉强能搞个 isCalling flag,群聊直接懵圈:谁加入了、谁拒绝了、谁掉线了、谁在响铃中、视频轨道谁发布了……状态一多,Vue 的响应式系统开始疯狂重渲染,内存泄漏和竞态条件接踵而至。
3. UI 实现成本被严重低估
视频通话的 UI 不是简单放个 <video> 标签。单聊要有悬浮窗、最小化、拖拽、画中画;群聊要有九宫格、主视频模式、说话者高亮、成员管理。这些交互写起来没半个月下不来。
4. 单聊和群聊像是两个世界
单聊是二元状态机(呼叫方 ↔ 被叫方),群聊是分布式参与者集合。两者的信令协议、状态模型、UI 布局完全不同,很多团队最后不得不维护两套代码。
🎯 CallKit 解决什么问题?一句话:把上面这些坑全部填平
Easemob Chat CallKit Vue3 是环信官方推出的音视频通话 UI 组件库,基于 Vue 3 + 环信 IM SDK + 声网 RTC SDK,把信令、状态、UI 全部封装好,开发者只需要关心三件事:
- 我已经登录了环信 IM(你本来就要做聊天功能对吧?)
- 我要呼叫谁(传一个 userId 或 groupId)
- 我要监听什么事件(通话结束记个时长、写条消息)
其他的一切——信令收发、RTC 频道管理、视频渲染、邀请弹窗、通话计时、静音/摄像头切换——全部内置。
🚀 5 分钟接入:从安装到打通第一通电话
安装依赖
# 安装 CallKit(以及你项目里已有的 IM 和 RTC SDK)
pnpm add easemob-chat-callkit-vue3 easemob-websdk agora-rtc-sdk-ng
Step 1:注册插件
// main.ts
import { createApp } from 'vue'
import EasemobChatCallKit from 'easemob-chat-callkit-vue3'
import 'easemob-chat-callkit-vue3/style.css'
import App from './App.vue'
const app = createApp(App)
app.use(EasemobChatCallKit)
app.mount('#app')
Step 2:在根组件放置 Provider + 通话组件
<template>
<EasemobChatCallKitProvider :chat-client="chatClient" :init-config="{ logLevel: 2 }">
<!-- 你的应用内容 -->
<router-view />
<!-- 被叫邀请弹窗(自动弹出,无需 v-if) -->
<InvitationNotification />
<!-- 单人通话组件(自动显隐) -->
<EasemobChatSingleCall />
<!-- 群组通话组件(自动显隐) -->
<EasemobChatMultiCall :group-id="currentGroupId" />
</EasemobChatCallKitProvider>
</template>
<script setup lang="ts">
import {
EasemobChatCallKitProvider,
InvitationNotification,
EasemobChatSingleCall,
EasemobChatMultiCall,
} from 'easemob-chat-callkit-vue3'
// 你已经有的环信 IM 实例
const chatClient = /* 你的 easemob-websdk Connection */
const currentGroupId = /* 当前群组 ID */
</script>
Step 3:发起通话
<script setup lang="ts">
import { useCallKit } from 'easemob-chat-callkit-vue3'
const { call, groupCall, hangup, accept, reject } = useCallKit()
// ── 发起 1v1 视频通话 ──
await call({
targetId: 'user123',
type: 'video',
userInfo: {
nickname: '张三',
avatarURL: 'https://example.com/avatar.png'
}
})
// ── 发起群组视频会议 ──
await groupCall({
groupId: 'group001',
members: ['user1', 'user2', 'user3'],
type: 'video',
groupName: '产品周会',
})
// ── 挂断 ──
await hangup()
</script>
就这三步。 不需要手动创建 RTC 频道,不需要处理信令消息,不需要写 v-if 控制组件显示隐藏。EasemobChatSingleCall 会根据内部状态自动出现和消失。
🎧 监听通话生命周期,集成到你的业务
通话结束后想记个时长?收到来电想响个铃?用 useCallKitEvents() 一站式订阅:
import { useCallKitEvents, HANGUP_REASON } from 'easemob-chat-callkit-vue3'
import { onUnmounted } from 'vue'
const {
onCallStarted,
onCallEnded,
onIncomingCall,
onCallRefused,
getCallRecord, // ← 自动生成标准化通话记录
} = useCallKitEvents()
// 通话接通
const unbind1 = onCallStarted((e) => {
console.log('通话开始', e.callId, '频道:', e.channel)
})
// 通话结束 → 自动获取通话记录,插入消息列表
const unbind2 = onCallEnded((e) => {
const sec = Math.round(e.duration / 1000)
console.log('通话结束,时长:', sec, '秒,原因:', e.reason)
// 直接拿到标准化记录,无需自己拼凑字段
const record = getCallRecord()
// record = { callId, conversationId, chatType, from, to, status, duration, timestamp, endedBy }
// 可以直接插入本地消息或发送 custom 消息
})
// 组件卸载时解绑,防止内存泄漏
onUnmounted(() => { unbind1(); unbind2() })
所有事件都携带 conversationId、isLocal、localUserRole 字段——你再也不用自己推断"这是单聊还是群聊""这是本端触发还是对端触发"。
🏗️ 设计思路:为什么 CallKit 能做得这么薄?
很多开发者会担心:"封装得这么彻底,灵活性会不会很差?" 不会。CallKit 的架构设计核心就一句话:"该隔离的隔离,该共享的共享"。
四层架构模型
┌─────────────────────────────────────────────┐
│ UI 层(完全隔离) │
│ SingleCall (1v1 悬浮窗) │ GroupCallShell │
│ (自动显隐/拖拽/画中画) │ (九宫格/主视频) │
└─────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────┐
│ 状态层(领域隔离 + 共享) │
│ SingleCallStore │ GroupCallStore │
│ (二元状态机) │ (分布式参与者集合) │
│ ↕ │
│ GlobalCallStore(跨域共享) │
│ userInfoMap / isMinimized │
└─────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────┐
│ 服务层(无状态,纯原子能力) │
│ SignalingService │ RtcChannelService │
│ (发/收信令) │ (join/leave/track) │
└─────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────┐
│ 基础设施层(外部 SDK) │
│ 环信 IM SDK │ 声网 RTC SDK │
└─────────────────────────────────────────────┘
关键设计决策
| 层级 | 策略 | 理由 |
|---|---|---|
| UI 层 | 单聊/群聊彻底隔离 | 单聊是"一对一窗口+拖拽",群聊是"多方网格+主视频",交互模式差异巨大,强行复用只会增加复杂度 |
| 状态层 | 领域隔离 + GlobalCallStore 共享 | 单聊是二元状态机(IDLE → INVITING → IN_CALL → IDLE),群聊是分布式参与者集合;但 userInfoMap(头像/昵称)、isMinimized 需要跨域共享 |
| 服务层 | 无状态共享 | sendInviteMessage、joinChannel、createAudioTrack 是通用能力,不应绑定任何业务状态 |
| 基础设施 | 单实例共享 | IM 连接和 RTC 客户端各一个实例,避免资源浪费 |
自动显隐:开发者不用写 v-if
单聊组件内置了状态驱动的显隐逻辑:
INVITING(呼叫中)→ 显示等待界面ALERTING(被叫响铃)→ 不显示,由InvitationNotification接管弹窗IN_CALL(通话中)→ 显示视频流界面IDLE(空闲)→ 自动隐藏
这意味着你把组件往模板里一放,剩下的交给 CallKit。
类型安全的事件系统
不像传统 EventBus 的 any 类型,useCallKitEvents() 提供完全类型化的事件订阅:
onCallEnded((e) => {
e.reason // HANGUP_REASON 枚举,有代码提示
e.duration // number,毫秒
e.conversationId // string,单聊=对方ID,群聊=groupId
e.isLocal // boolean,true=本端挂断
})
每个事件都返回解绑函数,组件卸载时自动清理,告别内存泄漏。
过期信令自动过滤
多端登录、离线重连时,过期的邀请/取消信令会堆积。CallKit 内置了时间戳过期判断(invite 40s 阈值,cmd 60s 阈值),自动丢弃过期消息,避免"幽灵弹窗"。
📦 更多能力一览
| 特性 | 说明 |
|---|---|
| 📞 单人通话 | 1v1 音频/视频,支持呼叫、接听、拒绝、挂断 |
| 👥 群组通话 | 多人音视频,支持邀请成员、视频网格布局 |
| 🔔 邀请通知 | 被叫方自动弹出接听/拒绝弹窗 |
| 🎛️ 媒体控制 | 静音、开关摄像头、切换前后置 |
| 🖼️ 视频布局 | 单聊悬浮窗+最小化;群聊九宫格/主视频模式 |
| 🎯 自动显隐 | 根据通话状态自动显示/隐藏,无需手动 v-if |
| 📊 通话记录 | getCallRecord() 自动生成标准化记录 |
| 🔧 源码调试 | 支持 Vite alias 映射到源码,开发时热更新 |
📝 适用场景
- 社交 App:1v1 语音/视频通话、多人语音房
- 在线教育:1v1 答疑、小班课视频互动
- 远程医疗:医患视频问诊
- 企业协作:群组视频会议、远程面试
- 任何已有环信 IM 的项目:聊天功能已经用了环信,直接叠加通话能力
😁效果图片
待接听
视频通话中
群组通话中
被叫待接听
🔗 相关链接
| 资源 | 链接 |
|---|---|
| 📘 GitHub 仓库 | github.com/Easemob-Com… |
| 📖 完整 API 文档 | USAGE.md |
| 🚀 体验地址 | 线上地址 |
| 🏢 环信官网注册 | www.easemob.com/ |
| 📦 npm 包 | easemob-chat-callkit-vue3 |
💡 写在最后
音视频通话是现代应用的标配,但自建成本极高。Easemob Chat CallKit Vue3 的思路是:把通用能力下沉到组件库,把业务逻辑留给开发者。你只需要关心"什么时候发起通话"和"通话结束后做什么",中间最复杂的信令协商、状态同步、视频渲染,全部封装在组件内部。
如果你正在用 Vue3 开发即时通讯应用,或者正为集成音视频通话头疼,不妨试试这个方案。注册环信账号、创建应用、获取 App Key,几分钟就能跑通第一通电话。
环信官网注册入口 👉 www.easemob.com/
注册后即可创建应用,免费体验完整的 IM + 音视频通话能力。
本文介绍的 Easemob Chat CallKit Vue3 基于 MIT 协议开源,欢迎 Star 和 PR。