小张同学
创建组件-装备路由
(1) 创建组件user/chat.vue
<template>
<div class="chat">小张机器人</div>
</template>
<script>
export default {}
</script>
<style></style>
(2) 装备路由
{
path: '/user/chat',
component: Chat
},
(3) 根本结构与样式
<template>
<div class="container">
<van-nav-bar
fixed
left-arrow
@click-left="$router.back()"
title="小张同学"
></van-nav-bar>
<div class="chat-list">
<!-- 左边是机器人小张 -->
<div class="chat-item left">
<van-image fit="cover" round src="https://www.6hu.cc/wp-content/uploads/2024/04/241682-QXRC3Z.jpeg" />
<div class="chat-pao">hi,你好!</div>
</div>
<!-- 右侧是当时用户 -->
<div class="chat-item right">
<div class="chat-pao my">ewqewq</div>
<van-image fit="cover" round src="https://www.6hu.cc/wp-content/uploads/2024/04/241682-QXRC3Z.jpeg" />
</div>
</div>
<div class="reply-container van-hairline--top">
<van-field v-model.trim="word" placeholder="说点什么...">
<span @click="send()" slot="button" style="font-size:12px;color:#999">
提交
</span>
</van-field>
</div>
</div>
</template>
<script>
export default {
name: 'UserChat',
data() {
return {
word: ''
}
},
methods: {
send() {
console.log(this.word)
}
}
}
</script>
<style lang="less" scoped>
.container {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
box-sizing: border-box;
background: #fafafa;
padding: 46px 0 50px 0;
.chat-list {
height: 100%;
overflow-y: scroll;
.chat-item {
padding: 10px;
.van-image {
vertical-align: top;
width: 40px;
height: 40px;
}
.chat-pao {
vertical-align: top;
display: inline-block;
min-width: 40px;
max-width: 70%;
min-height: 40px;
line-height: 38px;
border: 0.5px solid #c2d9ea;
border-radius: 4px;
position: relative;
padding: 0 10px;
background-color: #e0effb;
word-break: break-all;
font-size: 14px;
color: #333;
&::before {
content: '';
width: 10px;
height: 10px;
position: absolute;
top: 12px;
border-top: 0.5px solid #c2d9ea;
border-right: 0.5px solid #c2d9ea;
background: #e0effb;
}
}
.chat-pao.my {
background-color: #9eea6a;
&::before {
content: '';
background: #9eea6a;
}
}
}
}
}
.chat-item.right {
text-align: right;
.chat-pao {
margin-left: 0;
margin-right: 15px;
&::before {
right: -6px;
transform: rotate(45deg);
}
}
}
.chat-item.left {
text-align: left;
.chat-pao {
margin-left: 15px;
margin-right: 0;
&::before {
left: -5px;
transform: rotate(-135deg);
}
}
}
.reply-container {
position: fixed;
left: 0;
bottom: 0;
height: 44px;
width: 100%;
background: #f5f5f5;
z-index: 9999;
}
</style>
头像处理
(1) 小张头像
vant-contrib.gitee.io/vant/#/zh-C…
运用van-image处理本地资源时,需求额定处理。
<van-image fit="cover" round :src="require('@/assets/avatar.png')" />
(2) 处理自己的头像
<van-image fit="cover" round :src="$store.state.user.userInfo.photo" />
(3)进入组件,获取个人信息
created() {
if (!this.userInfo.photo) {
this.Actions_getUserInfo()
}
},
methods: {
...mapActions('user', ['Actions_getUserInfo']),
}
聊天信息数据结构且渲染
-
约好数据结构
list: [
// type: 1 小张的音讯
// type: 2 用户发的音讯
{
type: 1,
msg: '你好,我是小张',
timestamp: Date.now()
},
{
type: 2,
msg: '我是编程小王子!',
timestamp: Date.now()
},
{
type: 1,
msg: '你以为会在百度上抄代码,便是程序员了吗?',
timestamp: Date.now()
},
{
type: 2,
msg: '不是这样吗?',
timestamp: Date.now()
}
]
-
渲染
<div class="chat-list">
<div v-for="(item, index) in list" :key="index">
<!-- 左边是机器人小张 -->
<div class="chat-item left" v-if="item.type === 1">
<van-image fit="cover" round :src="require('@/assets/avatar.png')" />
<div class="chat-pao">{{item.msg}}</div>
</div>
<!-- 右侧是当时用户 -->
<div class="chat-item right" v-else>
<div class="chat-pao my">{{item.msg}}</div>
<van-image fit="cover" round :src="$store.state.user.userInfo.photo" />
</div>
</div>
</div>
websocket
WebSocket 是一种数据通信协议,类似于咱们常见的 http 协议。
为什么需求 WebSocket?
初度触摸 WebSocket 的人,都会问相同的问题:咱们已经有了 HTTP 协议,为什么还需求另一个协议?它能带来什么优点?
答案很简单,由于 HTTP 协议有一个缺点:通信只能由客户端发起。
举例来说,咱们想了解今天的气候,只能是客户端向服务器宣布恳求,服务器回来查询结果。HTTP 协议做不到服务器主意向客户端推送信息。
这种单向恳求的特色,注定了假如服务器有连续的状况改变,客户端要获悉就十分费事。咱们只能运用“轮询”:每隔一段时分,就宣布一个问询,了解服务器有没有新的信息。最典型的场景便是聊天室。
轮询的效率低,十分浪费资源(由于必须不断衔接,或许 HTTP 衔接始终翻开)。因此,工程师们一直在思考,有没有更好的办法。WebSocket 便是这样发明的。
websocket简介
WebSocket 协议在2008年诞生,2011年成为国际标准。一切现代浏览器都已经支撑了。
它的最大特色便是,服务器可以主意向客户端推送信息,客户端也可以主意向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
典型的websocket应用场景:
- 即时通讯、客服
- 聊天室
-
点餐
websocket运用-原生
根本过程
- 浏览器宣布链接恳求
- 服务器告知链接成功
- 双方进行双向通讯
-
封闭衔接
中心api
// 翻开websocket衔接
// WebSocket 是浏览器的内置对象
var ws = new WebSocket('wss://echo.websocket.org') // 树立与服务端地址的衔接
// 假如与服务器树立衔接成功, 调用 websocket实例的 回调函数 onopen
ws.onopen = function () {
// 假如履行此函数 表明与服务器树立联系成功
}
// 发送音讯
ws.send('音讯')
// 接纳音讯
ws.onmessage = function (event) {
// event中的data便是服务器发过来的音讯
}
// 封闭衔接
ws.close()
// 封闭衔接成功
ws.onclose = function () {
}
示例demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>体会websocket</title>
<style>
#contanier {
width: 500px;
height: 400px;
border: 2px dashed #7575e7;
overflow-y: auto;
}
</style>
</head>
<body>
<div id="contanier"></div>
<!-- 1 树立衔接 (拨号) -->
<!-- 2 发音讯 接音讯 -->
<!-- 3 封闭衔接 -->
<input type="text" id="message" />
<button id="btn1" >树立衔接</button>
<button id="btn2" >发送音讯</button>
<button id="btn3" >封闭衔接</button>
<script>
var dom = document.getElementById('contanier')
var inputDom = document.getElementById('message')
var btn1 = document.getElementById('btn1')
var btn2 = document.getElementById('btn2')
var btn3 = document.getElementById('btn3')
var isOpen = false // 表明是否已经树立了拨号
var ws // 其他办法 也需求运用ws
// 翻开websocket衔接
btn1.onclick = function () {
/// 网络上提供的一个测试websocket功用的服务器地址。
/// 它的效果是,你向服务器发什么音讯 ,它就完全回复还给你。
ws = new WebSocket('wss://echo.websocket.org') // 树立与服务器的联系
// onopen是webSocket约好事情名
// 当本地客户端浏览器与服务器树立衔接之后,就会履行onopen的回调
ws.onopen = function (event) {
isOpen = true
// 树立成功
dom.innerHTML = dom.innerHTML + `<p>与服务器成功树立衔接</p>`
}
// 接纳音讯
// onmessage是webSocket约好事情名
// 假如从服务器上发过来了音讯,则会进入onmessage的回调
ws.onmessage = function (event) {
// 由于 咱们先给服务器发了音讯 服务器给咱们回了音讯
dom.innerHTML =
dom.innerHTML + `<p>服务器说:${event.data}</p>`
}
// onclose是webSocket约好事情名
ws.onclose = function () {
// 此函数表明 封闭衔接成功
isOpen = false // 把状况封闭掉
dom.innerHTML = dom.innerHTML + `<p>与服务器衔接封闭</p>`
}
}
// 发送音讯 接纳音讯
btn2.onclick = function () {
if (inputDom.value && isOpen) {
// 发音讯 要等到 衔接成功才能发 并且内容不为空
// 发音讯便是send
ws.send(inputDom.value) // 发送音讯
// 发完之后 添加到 当时视图上
dom.innerHTML =
dom.innerHTML + `<p>我说:${inputDom.value}</p>`
inputDom.value = ''
}
}
// 封闭衔接
btn3.onclick = function () {
ws.close() // 封闭衔接
}
</script>
</body>
</html>
socket.io 的运用
原生的 WebSocket 运用比较费事,所以推荐运用一个封装好的解决方案
:socket.io
- socket.io是一套解决方案:即有前端也有后端;对各种不同的言语都有支撑。
- 在写前端代码时,只需引进它的客户端即可。
- 装置包 socket.io-client 导入运用
import io from 'socket.io-client'
- 树立衔接
const socket = io('地址',{额定传参})
等同于 原生websocketnew WebSocket()
- 发音讯:
socket.emit('自定义音讯名', '内容');
- 收音讯:
socket.on('自定义音讯名', function(msg){}
-
封闭链接:
socket.close()
小张同学-通讯
根本完成
-
装置依靠包, 本项目中,不用考虑服务器端代码,因此只需求装置客户端的包
npm i socket.io-client
# or
yarn add socket.io-client
-
导入依靠包
import io from 'socket.io-client'
-
组件初始化时,衔接websocket
created() {
this.$store.dispatch('user/getUserInfo')
// 链接服务器
const socket = io('http://toutiao.itheima.net', {
query: {
token: this.$store.state.user.token.token
},
transports: ['websocket']
})
this.socket = socket
// 当衔接上服务器就会触发
socket.on('connect', () => {
this.$toast.success('衔接服务器成功')
})
// 当服务器给咱们发送音讯会触发
socket.on('message', (data) => {
console.log('服务器给我的音讯', data)
this.list.push({
type: 1,
msg: data.msg,
timestamp: data.timestamp
})
})
}
-
点击按钮时,发送音讯给小张
send() {
if (!this.word) {
return this.$toast('请输入聊天的内容')
}
this.list.push({
type: 2,
msg: this.word,
timestamp: Date.now()
})
this.socket.emit('message', {
msg: this.word,
timestamp: Date.now()
})
this.word = ''
}
}
-
回车发送功用
<div class="reply-container van-hairline--top">
<van-field
@keyup.enter="send"
v-model.trim="word"
placeholder="说点什么..."
>
<span @click="send" slot="button" style="font-size:12px;color:#999">
提交
</span>
</van-field>
</div>
组件毁掉的时分
beforeDestroy() {
this.socket.close()
},
滚动到底部
在对话时,内容区域滚动条不能自己到达底部
watch: {
async list() {
// 监听list数组的改变 ==> 一旦list改变了,把chat-list滚动到最底部 ==> 修改其chat-list的scrollTop,给一个大值
// console.log('list 改变了')
await this.$nextTick()
// this.$refs.chatBox.scrollTop = 1000000000000000
// 以上代码换种写法
this.$refs.chatBox.scrollTop = this.$refs.chatBox.scrollHeight
}
},