前言
在我们的项目中很多时候会用到缓存,例如系统的几个大模块,半年不见它更新一次,我们也许就会直接缓存起来,这样也算是一个优化,但是一年后要更新了,那我怎么去做这个自动更新呢, 今天就简单的谈下。
应用场景
假设我们在从某个接口获取到了头部的动态菜单导航栏菜单列表,页面如下,我们一般都直接缓存起来,不再去每次打开页面时再做新的请求,突然产品要加个新的导航xx进去,我们一般最简单的方式就是修改代码拿掉缓存直接发起新的请求。
计时器与时间戳计时更新
在我们的js中有常见的计时器,代码如下:
<script setup>
import {reactive} from "vue";
import axios from 'axios'
const TableData = reactive({
Menu: ''
})
const GetMenuList = () => {
axios.get(xxx).then(res => {
TableData.Menu = res.data;
})
}
setInterval(()=>{
GetMenuList();
console.log('每过10秒更新一次缓存')
},10000)
</script>
在我们的js中不单有计时器,还有时间戳,我们也可以通过相隔一定的时间内去做一定的更新,代码如下:
<script setup>
import {reactive, onMounted} from "vue";
import axios from 'axios'
const TableData = reactive({
Menu: ''
})
const GetMenuList = () => {
axios.get(xxx).then(res => {
// 在接口请求成功后,记录当前的时间戳,时隔一天后再做更新
localStorage.setItem('CurrentTime', Date.parse(new Date()))
TableData.Menu = res.data;
})
}
onMounted(()=>{
const BeforeTime = localStorage.getItem('CurrentTime')
const CurrentTime = Date.parse(new Date())
// 通过时间戳,每隔一天就更新一次
if (BeforeTime + 12 * 60 * 60 > CurrentTime) {
GetMenuList()
}
})
</script>
计时器更新,代码简单,而且不需要后端辅助,就是有点浪费内存,我们如果我们的间隔时间越来越小,那就更加浪费了,时间戳更新,比较缓慢,不适合做及时更新,倒适合做隔天更新这样的场景,不过也是比较占用内存
Websocket实时更新
我们经常会听到实时通讯websocket,websocket是一种全双工通信长连接,大多用来实现即时通信,数据实时性要求较为高的地方。相比较于统的http协议有点不同:
http: 在使用http请求时,必须由客户端主动发起请求,服务端才会发起响应,服务端给出响应之后,就代表这次的通信结束了,只能等待下次客户端再次发起请求才能建立通信通道。
websocket: 使用websocket时,采用的是TCP协议,TCP是一个双向通道,这就意味着,客户端与服务端之间的通信是自由的,服务端可以主动的向客户端发起响应,而且两者之间的通信建立之后一般不会结束,除非遇到特殊情况,否则会一直保持通信。
Websocket心跳机制:
- 在与客户端建立连接的时候,(客户端es6中websocket中的open方法)开始心跳
- 间隔一定的时间客户端通过send方法向服务器发送一个信息,再次进行通信(即再次心跳)
- 遭遇特殊情况,某次通信时双方无法完成应答,通过close 或者error事件来关闭通信
- close或者error之后关闭之前的连接,开始新的连接
实例:以nodejs与vue建立客户端与服务端间的实时通信
新建一个文件夹websocket,在该文件夹下新建一个文件夹nodeserve用来放置负端服务端nodejs文件,同时新建另一个文件夹vue_01用来放置客户端文件vue文件
nodeserve初始化
npm init -y
下载nodejs插件WS
npm install ws --save
npm install 安装下依赖
nodeserve文件夹中建立服务文件connection.js,代码如下:
// 引入插件ws
const Webscoket = require('ws');
// 建立本地通信,默认8000端口
const Serve = new Webscoket.Server({host: '127.0.0.1', port: 8000})
// connection 插件提供的方法,用于建立连接
Serve.on('connection', handleConnection)
function handleConnection(ws) {
console.log('开始建立通信');
// 通信建立完成之后的几种监听模式
ws.on('error', resolveError)
ws.on('close', resolveClose)
ws.on('message', resolveMessage)
ws.send(JSON.stringify({user:'杰瑞',msg:'汤姆你收到了吗,收到请回复'}))
}
function resolveError(e) {
console.log(e)
}
function resolveClose() {
console.log('通信关闭')
this.send(JSON.stringify('你好,客户端,我们现在要关闭通信了,期待下次见面'));
}
// 接受客户端发送来的信息
function resolveMessage(data) {
console.log('已接收到客户端发送来的信息:', data)
}
请注意:
- 这里是使用的端口是8000,一般默认就是8000,不建议使用其他的端口,不然会报错。
- ws自身提供了几种通信的监听方法,如:error close, message
- Websocket.serve的api如下,大家可以酌情使用:
host: 绑定服务器的主机名
port: 绑定服务器的端口号
backlog: 挂起连接队列的最大长度
server: 预先创建的nodejs http/s服务器
verifyClient: 可用于验证传入连接的函数
handleProtocols: 可用于处理WebSocket子协议的函数
path: 仅接受与此路径匹配的连接
noServer: 不启用服务器模式
clientTracking: 指定是否跟踪客户端
perMessageDeflate: 启用/禁用消息压缩
maxPayload: 允许的最大消息大小 (以字节为单位)
简单的服务端就搭建好了,这里我们利用nodemon来启动下我们的服务端,首先我们下载下nodemon
npm install nodemon --save
package.json
npm run serve这样我们的服务端就算是启动好了,接下来我们创建一个客户端文件
创建客户端socket
在websocket文件下通过命令行创建vue文件socket
vue create socket
App.vue
<template>
<img alt="Vue logo" src="./assets/logo.png">
<div style="width: 100%;text-align: center">
<button @click="sendMsg">点击发送信息到服务端</button>
</div>
</template>
<script setup>
import {onMounted} from "vue";
const sendMsg = () => {
console.log('你好服务端,这是我给你发送的信息')
}
// 建立与服务端之间的通信
const OtherConection = ()=>{
const socket = new WebSocket('ws://127.0.0.1:8000')
// 通过open方法建立与服务端之间的连接
socket.addEventListener('open',(e)=>{
console.log('链接通道打开',e)
// 建立通信之后,通过自带的send方法向服务端发送信息
socket.send({user:'汤姆',msg:'杰瑞,最近过的好吗'})
})
socket.addEventListener('message',(e)=>{
console.log('接收到服务端的信息如下:',e)
})
socket.addEventListener('error',(e)=>{
console.log(e)
})
socket.addEventListener('close',(e)=>{
console.log(e)
})
}
onMounted(() => {
OtherConection()
})
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
MDN对websocket的部分介绍如下:
事件
使用 addEventListener() 或将一个事件监听器赋值给本接口的 oneventname 属性,来监听下面的事件。
-
当一个
WebSocket连接被关闭时触发。 也可以通过onclose属性来设置。 -
当一个
WebSocket连接因错误而关闭时触发,例如无法发送数据时。 也可以通过onerror属性来设置。 -
当通过
WebSocket收到数据时触发。 也可以通过onmessage属性来设置。 -
当一个
WebSocket连接成功时触发。 也可以通过onopen属性来设置。
示例
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
同时启动服务端与客户端效果如下:
客户端:
可以看到我们这里已经收掉了服务端的来信
服务端:
额,格式有点问题,而且接收到的信息是buffer类型,还需要再改改
App.vue
// 通过open方法建立与服务端之间的连接
socket.addEventListener('open',(e)=>{
console.log('链接通道打开',e)
// 建立通信之后,通过自带的send方法向服务端发送信息
socket.send(JSON.stringify({user:'汤姆',msg:'杰瑞,最近过的好吗'}))
})
connection.js
// 接受客户端发送来的信息
function resolveMessage(data) {
console.log('已接收到客户端发送来的信息:', data)
const jsonString = data.toString('utf-8'); // 将Buffer对象转换为字符串(假设Buffer对象中的数据是以UTF-8编码的字符串)
const jsonObj = JSON.parse(jsonString); // 将字符串转换为JSON对象
const jsonStringAgain = JSON.stringify(jsonObj); // 再将JSON对象转换为字符串(如果需要的话)
console.log(JSON.parse(jsonStringAgain));
}
再来看下效果:
可以看到我们服务端与客户端之间的连接已经可以正常通话了。
我们来添加一个关闭按钮模拟下关闭通信,通过status来判断当前的通信状态
<template>
<img alt="Vue logo" src="./assets/logo.png">
<div style="width: 100%;text-align: center">
<button @click="sendMsg">点击发送信息到服务端</button>
<button @click="closeMsg" style="margin-left: 100px">关闭通信</button>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue";
const status = ref(false) // 判断通信的连接状态
const sendMsg = () => {
console.log('你好服务端,这是我给你发送的信息')
const CanSend = status.value;
console.log(CanSend)
if(CanSend){
socket.send(JSON.stringify({user:'汤姆',msg:'老表快来啊,淄博烧烤真不错,一起搞点啊'}))
}else {
// 客户端重新发起请求
socket = new WebSocket('ws://127.0.0.1:8000')
status.value = true
}
}
const closeMsg = ()=>{
status.value = false
socket.close(1000);
}
// 建立与服务端之间的通信
let socket = new WebSocket('ws://127.0.0.1:8000')
const OtherConection = ()=>{
// 通过open方法建立与服务端之间的连接
socket.addEventListener('open',(e)=>{
console.log('链接通道打开',e)
status.value= true
// 建立通信之后,通过自带的send方法向服务端发送信息
socket.send(JSON.stringify({user:'汤姆',msg:'杰瑞,最近过的好吗'}))
})
socket.addEventListener('message',(e)=>{
console.log('接收到服务端的信息如下:',e)
})
socket.addEventListener('error',(e)=>{
console.log(e)
})
socket.addEventListener('close',(e)=>{
console.log(e)
// 检测到关闭通信之后,建立新的通信连接
socket = new WebSocket('ws://127.0.0.1:8000')
status.value = true
})
}
onMounted(() => {
OtherConection()
})
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
connection.js
// 引入插件ws
const Webscoket = require('ws');
// 建立通信
const Serve = new Webscoket.Server({host: '127.0.0.1', port: 8000})
// resolveMessage
Serve.on('connection', handleConnection)
function handleConnection(ws) {
console.log('开始建立通信');
// 通信建立完成之后的几种监听模式
ws.on('error', resolveError)
ws.on('close', resolveClose)
ws.on('message', resolveMessage)
ws.send(JSON.stringify({user:'杰瑞',msg:'汤姆你收到了吗,收到请回复'}))
}
function resolveError(e) {
console.log(e)
}
function resolveClose(e) {
console.log('通信关闭')
console.log(e)
}
// 接受客户端发送来的信息
function resolveMessage(data) {
console.log('已接收到客户端发送来的信息:', data)
this.send(JSON.stringify({uesr:'杰瑞',msg:'老表我已经收到你的信息了,等我过去吧'}))
const jsonString = data.toString('utf-8'); // 将Buffer对象转换为字符串(假设Buffer对象中的数据是以UTF-8编码的字符串)
const jsonObj = JSON.parse(jsonString); // 将字符串转换为JSON对象
const jsonStringAgain = JSON.stringify(jsonObj); // 再将JSON对象转换为字符串(如果需要的话)
console.log(JSON.parse(jsonStringAgain));
}
当我们点击关闭时,可以看到客户端重新发起了请求,当我们点击“发送信息到服务端”时,通信通道依然畅通
点击关闭
点击发送信息
总结: websocket能够自由双向通信,即使连接断开也可以重新连接,能够保证数据及时更新,而且websocket不存跨域的问题,缺点就是可能需要后端的帮助,后端要增加一条接口给前端使用。
利用其他接口数据进行提示更新
假设我们有四个模块ABCD,每当点击新的模块时,初始化页面接口中添加一个标识,所返回的数据中有一个key为flage当这个key发生变化时我们就重新更新缓存
<script setup>
import axios from "axios";
// 模块A初始化页面调用就接口
axios.get('www.xxxxx').then(res => {
const NeedUpdate = res.data.flag
if(NeedUpdate){
// 调用菜单栏接口进行重新缓存
}
})
</script>
websocket代码地址:github.com/wyk-6987/WE…