用socket io 来完成实时通信。
首先 先安装socket 依赖
在控制台中输入以下任意一个命令行。
这里我用的第二种
npm install vue-socket.io --save
npm install socket.io-client --save
配置文件
安装完成后
创建一个仓库在默认路径下与static同级创建一个store文件夹,文件夹下有index.js。index.js中用于写各种vuex中的逻辑问题
import Vue from 'vue'
import Vuex from 'vuex'
import io from '@hyoga/uni-socket.io';
import conf from '@/common/lib/config.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//仓库中的全局变量
isLoad: false,
socket:null,
lineState:false,
clientId:null,//对方
kefuId:null,
target:null,//目标
chatList:[],
msg:null,
action:null,
},
// 计算属性 可以获得参数,第一个是state,第二个是getters(自身) 用户传参(state,getters)=> (id)=>{}
getters: {
getClientId: state => state.clientId,
getKeFuId: state => state.kefuId,
getTarget: state => state.target,
getChatList: state => state.chatList,
getMsg: state => state.msg,
getAction: state => state.action
},
// 动态改变变量值
mutations: {
//示例
setIsLoad(state, isLoad) {
state.isLoad = isLoad
},
},
actions: {
//用于写异步方法
//链接socketIO
connectSocket({state, dispatch},data) {
// 这里的conf.socketUrl往下有提到 连接后赋值给socket
const socket = io(conf.socketUrl, {
//这里是连接时所携带的数据
query: data,
//连接方式
transports: [ 'websocket' ],
//过期时间
timeout: 5000,
});
//socket监听服务器发送的消息 服务器中一定有emit(‘connect’)。
socket.on('connect', () => {
console.log('ws 已连接');
//将连接成功的socket 赋值给变量 state.socket
state.socket = socket
//连接成功后,将上线状态改为true
state.lineState = true
// socket.io 唯一连接id,可以监控这个id实现点对点通讯
const { id } = socket;
//将连接的socket 即id 赋值给clientId (房间号)
state.clientId = id
//派发事件
const chatPush = (e) => {
uni.$emit('chatPush', e)
}
//派发没有分配客服的事件, 在index.vue页面引用 用:
const noKefu = () => {
uni.$emit('noKefu')
}
// 监听服务器连接成功后的状态
socket.on(id, (message, fn) => {
//连接成功后,接受服务器返回过来的信息
state.msg = message
const action = message.data.action
//连接成功后 获取返回过来的的action
state.action = action
const data = message.data
console.log('action:',action);
console.log('ws data:', data);
switch (action){
case 'online':
uni.showToast({
title:"连接成功",
icon:'none'
})
state.kefuId = message.data.payload.kefuId
break;
case 'no':
uni.showToast({
title:"没有可分配客服",
icon:'none'
})
//这里调用上面派发的没有客服的方法是true,就关连接
noKefu()
//如果在线状态是
if(state.lineState) {
this.dispatch('closeSocket')
}
break;
case 'noline':
uni.showToast({
title:"对方不在线",
icon:'none'
})
break;
case 'exchange':
//当是exchange类型的消息,就添加消息至消息列表
// 拿到数据传出去
// console.log(123);
chatPush(data)
break;
}
// 收到服务器推送的消息,可以跟进自身业务进行操作
if(typeof fn === 'function') {
fn('ok')
}
});
// 主动向服务器发送数据
// socket.emit('send_data', {
// time: +new Date(),
// });
});
//监听服务器的 在线 方法
socket.on('online', (msg) => {
console.log(msg);
});
//监听服务器的断开连接方法
socket.on('disconnect', () => {
console.log("断开链接");
if(state.lineState) {
this.dispatch('closeSocket')
}
});
//监听服务器出现错误的方法
socket.on('error', (msg) => {
console.log('ws error', msg);
if(state.lineState) {
this.dispatch('closeSocket')
}
});
},
//关闭连接
closeSocket({state, dispatch}) {
const offChat = ((e) => {
uni.$off('chatPush', e)
})
state.lineState === false
offChat()
if(state.socket !== null) {
state.socket.close()
state.socket = null
state.lineState = false
state.target = ''
}
},
//发送信息
sendMsg({state, dispatch}, data) {
console.log(data);
state.socket.emit('exchange', data)
}
}
})
在common的config文件中定义socket io链接地址
export default{
//这里是请求接口的地址
baseUrl:"http://wech******ip.com",
//这里是socket io通信地址
socketUrl:"http://we***********m/chat",
env:'dev'
}
这里的common/config.js文件是自己创建的。主要作用是存放请求地址,在common的文件夹下还有一个与config同级的文件request.js是用来写异步请求方法的,文章主题不是它,这里就不赘述了。request中与本文相关的内容就是,在request.js中引入vuex的仓库,用于连接仓库和页面
// 主要看这个地方,在这个文件里引入仓库
import $store from '@/store/index.js'
import $config from '@/common/lib/config.js'
export default {
// 全局配置
common: {
baseUrl: $config.baseUrl,
header: {
'Content-Type': 'application/json;charset=UTF-8',
'Content-Type': 'application/x-www-form-urlencoded',
},
data: {},
method: 'GET',
dataType: 'json'
},
// 请求 返回promise
request(options = {}) {
let token = true
options.url = this.common.baseUrl + options.url
options.header = options.header || this.common.header
options.data = options.data || this.common.data
options.method = options.method || this.common.method
options.dataType = options.dataType || this.common.dataType
if(options.token === false) {
token = false
}
//在这里判断token的时候应用$store
if(token) {
if(!$store.state.user.token) {
console.log('没有token');
return uni.reLaunch({
url:'/pages/login/login'
})
}
options.header.Authorization = `Bearer ${$store.state.user.token}`
}
return new Promise((resolve, reject) => {
// 请求前
// 请求中
uni.request({
...options,
success: (res) => {
// 服务端失败
if(res.statusCode !==200){
uni.showToast({
title:res.data.msg || '服务端请求失败',
icon:'none'
})
return reject()
}
let data = res.data
resolve(data)
},
fail: (err) => {
uni.showToast({
title:err.errMsg || '请求失败',
icon:'none'
})
return reject(err)
}
})
})
},
// get 请求
get(url, data = {}, options = {} ){
options.url = url
options.data = data
options.method = 'GET'
return this.request(options)
},
// post 请求
post(url, data = {}, options = {} ){
options.url = url
options.data = data
options.method = 'POST'
return this.request(options)
},
// post 请求
put(url, data = {}, options = {} ){
options.url = url
options.data = data
options.method = 'PUT'
return this.request(options)
},
// post 请求
del(url, data = {}, options = {} ){
options.url = url
options.data = data
options.method = 'DELETE'
return this.request(options)
}
}