Ably 免费版纯前端单聊
前言:对于前端开发如果实现即时通讯需要对接第三方例如腾讯IM,环信等。或者使用websocket,需要后台部署。
可是有些客户项目仅仅需要一个简单的单聊功能,没有第三方哪些复杂的功能。如果我们去对接第三方或者websokect显得有点大题小用,成本也随之增高。客户接受不了。
例如腾讯IM对于免费版还有人员限制,非常不适合这种简单的项目需求。
下面是一款免费的,仅仅需要前端就可以实现单聊的插件,非常适合小型单聊项目和开发人员测试使用,也可以根据项目需求进行自定义需求功能。
以下是页面效果图:支持移动端,pc web端
可是有些客户项目仅仅需要一个简单的单聊功能,没有第三方哪些复杂的功能。如果我们去对接第三方或者websokect显得有点大题小用,成本也随之增高。客户接受不了。
例如腾讯IM对于免费版还有人员限制,非常不适合这种简单的项目需求。
下面是一款免费的,仅仅需要前端就可以实现单聊的插件,非常适合小型单聊项目和开发人员测试使用,也可以根据项目需求进行自定义需求功能。
以下是页面效果图:支持移动端,pc web端


1.点击注册一个账号,qq和网易邮箱都支持。
2.复制API key
3.安装ably pnpm add ably @ably/chat 4.引入并使用,下面是demo测试完整代码:
<template>
<div class="chat">
<uv-navbar title="张三"></uv-navbar>
<div class="allcards">
<div v-for="(item,index) in messages" :key="index">
<div class="alone_left" v-if="item.sender=='张三'">
<image src="https://ww2.sinaimg.cn/mw690/006faMndly1htwlunnhn5j30zj0zjjv8.jpg" mode=""></image>
<div style="margin-left: 20rpx;">
{{item.text}}
</div>
</div>
<div class="alone_right alone_left" v-else>
<div style="margin-right: 20rpx;">{{item.text}}</div>
<image
src="https://img0.baidu.com/it/u=3974643408,1028378712&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800"
mode=""></image>
</div>
</div>
</div>
<div class="input-area">
<input class="input-area-input" v-model="input" @keyup.enter="sendMessage" placeholder="输入内容..." />
<button class="input-area-button" @click="sendMessage">发送</button>
</div>
</div>
</template>
<script setup lang="ts">
import {
ref,
onMounted
} from 'vue'
import * as Ably from 'ably'
// 修改为你自己的 Ably 公钥
const ABLY_API_KEY = 'xxx'
const client = new Ably.Realtime(ABLY_API_KEY)
const channel = client.channels.get('chat-room')
const input = ref('')
const messages = ref([]) as any
// 当前用户的名字
const userName = '李四'
function sendMessage() {
if (!input.value.trim()) return
const message = {
sender: userName,
text: input.value.trim()
}
channel.publish('message', message)
input.value = ''
}
onMounted(() => {
channel.subscribe('message', (msg) => {
messages.value.push(msg.data)
})
})
</script>
<style>
page {
background-color: #ededed;
}
.input-area {
width: 100%;
padding: 30rpx 0rpx;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 9;
background-color: #f7f7f7;
}
.input-area-input {
background-color: #fff;
width: 70%;
font-size: 26rpx;
padding: 16rpx;
border-radius: 10rpx;
margin-left: 24rpx;
}
.input-area-button {
width: 120rpx;
color: #fff;
background: #00c75a;
font-size: 24rpx;
margin-right: 24rpx;
outline: none;
border: none;
}
.allcards {
/* background-color: aqua; */
width: 95%;
height: 300rpx;
margin: 108rpx auto 0;
height: 80vh;
overflow-y: scroll;
}
.alone_left {
display: flex;
padding: 20rpx 0;
}
.alone_left image {
width: 60rpx;
height: 60rpx;
border-radius: 5rpx;
}
.alone_right {
justify-content: flex-end;
}
.alone_left div {
background-color: #fff;
font-size: 24rpx;
color: #333;
padding: 14rpx;
border-radius: 8rpx;
max-width: 70%;
}
.alone_right div {
background-color: #00c75a;
color: #fff;
}
</style>
<template>
<div class="chat">
<uv-navbar title="李四"></uv-navbar>
<div class="allcards">
<div v-for="(item,index) in messages" :key="index">
<div class="alone_left" v-if="item.sender=='李四'">
<image src="https://ww2.sinaimg.cn/mw690/006faMndly1htwlunnhn5j30zj0zjjv8.jpg" mode=""></image>
<div style="margin-left: 20rpx;">
{{item.text}}
</div>
</div>
<div class="alone_right alone_left" v-else>
<div style="margin-right: 20rpx;">{{item.text}}</div>
<image
src="https://img0.baidu.com/it/u=3974643408,1028378712&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800"
mode=""></image>
</div>
</div>
</div>
<div class="input-area">
<input class="input-area-input" v-model="input" @keyup.enter="sendMessage" placeholder="输入内容..." />
<button class="input-area-button" @click="sendMessage">发送</button>
</div>
</div>
</template>
<script setup lang="ts">
import {
ref,
onMounted
} from 'vue'
import * as Ably from 'ably'
// 修改为你自己的 Ably 公钥
const ABLY_API_KEY = 'xxx'
const client = new Ably.Realtime(ABLY_API_KEY)
const channel = client.channels.get('chat-room')
const input = ref('')
const messages = ref([]) as any
// 当前用户的名字(随机生成一个)
const userName = '张三'
function sendMessage() {
if (!input.value.trim()) return
const message = {
sender: userName,
text: input.value.trim()
}
channel.publish('message', message)
input.value = ''
}
onMounted(() => {
channel.subscribe('message', (msg) => {
messages.value.push(msg.data)
})
})
</script>
<style>
page {
background-color: #ededed;
}
.input-area {
width: 100%;
padding: 30rpx 0rpx;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 9;
background-color: #f7f7f7;
}
.input-area-input {
background-color: #fff;
width: 70%;
font-size: 26rpx;
padding: 16rpx;
border-radius: 10rpx;
margin-left: 24rpx;
}
.input-area-button {
width: 120rpx;
color: #fff;
background: #00c75a;
font-size: 24rpx;
margin-right: 24rpx;
outline: none;
border: none;
}
.allcards {
/* background-color: aqua; */
width: 95%;
height: 300rpx;
margin: 108rpx auto 0;
height: 80vh;
overflow-y: scroll;
}
.alone_left {
display: flex;
padding: 20rpx 0;
}
.alone_left image {
width: 60rpx;
height: 60rpx;
border-radius: 5rpx;
}
.alone_right {
justify-content: flex-end;
}
.alone_left div {
background-color: #fff;
font-size: 24rpx;
color: #333;
padding: 14rpx;
border-radius: 8rpx;
max-width: 70%;
}
.alone_right div{
background-color: #00c75a;
color: #fff;
}
</style>

浙公网安备 33010602011771号