小记《项目版本自动更新缓存》

625 阅读7分钟

前言

在我们的项目中很多时候会用到缓存,例如系统的几个大模块,半年不见它更新一次,我们也许就会直接缓存起来,这样也算是一个优化,但是一年后要更新了,那我怎么去做这个自动更新呢, 今天就简单的谈下。

应用场景

假设我们在从某个接口获取到了头部的动态菜单导航栏菜单列表,页面如下,我们一般都直接缓存起来,不再去每次打开页面时再做新的请求,突然产品要加个新的导航xx进去,我们一般最简单的方式就是修改代码拿掉缓存直接发起新的请求

1684236959151.png

计时器与时间戳计时更新

在我们的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

image.png

下载nodejs插件WS

npm install ws --save

npm install 安装下依赖

image.png

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)
}

请注意:

  1. 这里是使用的端口是8000,一般默认就是8000,不建议使用其他的端口,不然会报错。
  2. ws自身提供了几种通信的监听方法,如:error close, message
  3. Websocket.serve的api如下,大家可以酌情使用:
host: 绑定服务器的主机名
port: 绑定服务器的端口号
backlog: 挂起连接队列的最大长度
server: 预先创建的nodejs http/s服务器
verifyClient: 可用于验证传入连接的函数
handleProtocols: 可用于处理WebSocket子协议的函数
path: 仅接受与此路径匹配的连接
noServer: 不启用服务器模式
clientTracking: 指定是否跟踪客户端
perMessageDeflate: 启用/禁用消息压缩
maxPayload: 允许的最大消息大小 (以字节为单位)

简单的服务端就搭建好了,这里我们利用nodemon来启动下我们的服务端,首先我们下载下nodemon

npm install nodemon --save

image.png

package.json

image.png

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 属性,来监听下面的事件。

  • close

    当一个 WebSocket 连接被关闭时触发。 也可以通过 onclose 属性来设置。

  • error

    当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。 也可以通过 onerror 属性来设置。

  • message

    当通过 WebSocket 收到数据时触发。 也可以通过 onmessage 属性来设置。

  • open

    当一个 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);
});

同时启动服务端与客户端效果如下:

客户端:

image.png

可以看到我们这里已经收掉了服务端的来信

服务端:

image.png

额,格式有点问题,而且接收到的信息是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));
}

再来看下效果:

image.png 可以看到我们服务端与客户端之间的连接已经可以正常通话了。

我们来添加一个关闭按钮模拟下关闭通信,通过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));
}

当我们点击关闭时,可以看到客户端重新发起了请求,当我们点击“发送信息到服务端”时,通信通道依然畅通

点击关闭

image.png

点击发送信息

image.png

image.png

总结: 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…