客户端与服务器之间的双向通信
问题:怎么实现同时打开了A页面和B页面,两个页面有共用的数据内容,在A页面请求接口对数据库进行一波操作后,B页面对应相同的数据也要实时更新?
一、方式1
ajax暴力轮询,代码如下:
<!--B.vue-->
//引用请求库
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
<template>
...
</template>
export default {
name: 'B',
data() {
return {
data: ''
}
},
mounted() {
let timer = setInterval(()=>{
this.getData()
},3000)
},
methods: {
//请求数据
getData(){
Vue.axios.get('/api/test')
.then( res => {
console.log(res)
this.data = res.data;
})
.catch( error => {
console.log(error)
})
}
}
}

代码中每隔3s向服务器发送请求,获取最新数据后使用。很明显,在数据变化不是很频繁的时候,这种方式的请求有大部分请求是无用的,很大的浪费带宽和服务器资源。对服务端造成了很大压力。
二、方式2
websocket,是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议,能够实现客户端和服务器实时交互,相互通信.浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输,实现实时数据更新。

因为原生 websocket 支持到IE11,有兼容问题,实际开发中,常用 socket.io ,它对原始的 API 做了进一步封装和兼容IE处理,提供了更多功能,分为客户端和服务端的实现。下文中客户端使用的是vue封装的一个库 vue-socket.io ,服务器结合 express 引用 socket.io 模块。
使用说明:
- 客户端
- 安装依赖
npm install vue-socket.io --save
npm install socket.io-client --save
- main.js中引用
import VueSocketio from 'vue-socket.io';
import socketio from 'socket.io-client';
...
Vue.use(VueSocketio, socketio('http://localhost:3000/')); //url为服务器请求地址
- vue组件使用
<!--SocketTest.vue-->
<template>
...
</template>
export default {
name: 'SocketTest',
data() {
return {
...
}
},
//此处若使用 methods 里方法,需注意this指向问题,可以用this.$options.methods + bind解决
sockets: {
//socket连接
connect: ()=> {
console.log('client socket connected')
},
//客户端监听服务器自定义事件serveEmit的响应
serveEmit: data=> {
console.log(data)
},
//断开连接
disconnect: ()=> {
console.log("client socket disconnected");
}
},
methods: {
clickButton: function (data) {
// $socket代指socket.io-client,客户端向服务器发送clientEmit事件
this.$socket.emit('clientEmit', { hello: 'I am from client' })
}
}
}
- 服务器
- 安装依赖
npm install socket.io --save
- serve.js 中使用socket.io
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(80);
io.on('connection', function (socket) {
console.log('server socket connected');
//服务器向客户端发送自定义事件serveEmit
socket.emit('serveEmit', { hello: 'I am from server' });
//服务器监听客户端发送自定义事件clientEmit
socket.on('clientEmit', function (data) {
console.log(data);
});
//断开连接
socket.on("disconnect", ()=> {
console.log("server socket disconnected");
});
});
不管是服务器还是客户端都有 emit 和 on 这两个函数,可以说 socket.io 的核心就是这两个函数了,通过 emit 和 on 可以地实现服务器与客户端之间的双向通信。
emit :用来发送一个事件,第一个参数为事件名,第二个参数为要发送的数据,第三个参数为回调函数,可省略,如需对方接受到信息后立即得到确认时,则需要用到回调函数。
on :用来监听一个 emit 发送的事件,第一个参数为要监听的事件名,第二个参数为一个匿名函数用来接收对方发来的数据,该匿名函数的第一个参数为接收的数据,若有第二个参数,则为要返回的函数。
socket.io 提供了三种默认的事件(客户端和服务器都有):connect 、message 、disconnect 。当与对方建立连接后自动触发 connect 事件,当收到对方发来的数据后触发 message 事件(通常为 socket.send() 触发),当对方关闭连接后触发 disconnect 事件。此外,socket.io 还支持自定义事件,毕竟以上三种事件应用范围有限,正是通过这些自定义的事件才实现了丰富多彩的通信。
最后,需要注意的是,在服务器端区分以下三种情况:
- socket.emit() :向建立该连接的客户端广播(一对一)
- socket.broadcast.emit() :向除去建立该连接的客户端的所有客户端广播(一对多)
- io.sockets.emit() :向所有客户端广播,等同于上面两个的和(多对多)
应用场景:聊天应用、购物商城、外卖平台等。