前端检测版本更新-Worker 项目实践

前言

单页应用(Single Page Application,简称SPA)项目中,通过动态重载页面的部分内容来提高项目流畅性。然而该应用也存在弊端,当服务端发生更新,接口请求体和响应体结构发生变化,停留在客户端的项目依然访问的旧资源,未能同步更新,就可能导致项目报错。对于浏览器长时间保持打开的用户来说,当版本更新提示客户版本升级就很有必要。

实现方式

1. WebSocket实时通信

  • 建立WebSocket连接,有新版本时服务器向客户发送通知
    需要后端配合,后端回复说就不能前端自己搞嘛

2. 前端检测版本更新

  • a) 比较构建文件的hash值
  • b) 利用HTTP协议的Etag 或 Last-modified
js 复制代码
const projectHome = '/'
// a) 比较构建文件的hash值
const getScriptHash = () => {
    fetch(projectHome).then((res) => {  
    const html = res.text();  
    return html.match(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/i)[0]  
    })
}
// '\x3Cscript type="module" crossorigin src="/Lance-Element-Admin/assets/index-56a14e29.js">\x3C/script>'

// b: 利用HTTP协议的Etag 或 Last-modified
const getVersionTag = async () => {  
    return fetch(projectHome, { method: 'HEAD', cache: 'no-cache' })  
    .then(res => res.headers.get('etag') || res.headers.get('last-modified'))  
    .catch(e => {  
    console.error('Failed to fetch version tag', e)  
    return null  
    })  
}
// etag: 'W/"67caae6b-578"'  last-modified: 'Fri, 07 Mar 2025 08:29:31 GMT'

3. 前端检测触发时机/方式

  1. 通过 setInterval, setTimeout 模拟 setInterval 轮询
  2. 监听 visibilitychange tab切换显示 触发
以下使用的是我们的 getVersionTag方法 来做校验为例
js 复制代码
let timer = null
const doFetch = async () => {
    const versionTag = await getVersionTag()  
    if (!versionTag) return  
    // 首次运行时不提示更新  
    if (!window.lastVersionTag) {  
        window.lastVersionTag = versionTag  
        return  
    }  
    if (window.lastVersionTag !== versionTag) {
        stop()
        console.log('项目更新了, 是时候刷新页面了!!!!')
        // todo 提示用户项目更新操作 +++
    }
}
const start = async (immediate = false) => {  
    if (immediate) {  
     await doFetch()  
    }
    if (timer) stop()
    timer = setInterval(doFetch, 5000)
}
const stop = () => {  
    clearInterval(timer)  
}

function handleVisibilitychange() {  
    if (document.hidden) {  
        stop()  
    } else {
        start(true)  
    }  
}
document.addEventListener('visibilitychange', handleVisibilitychange)
start()

以上我们的 前端版本更新校验主要逻辑已经初步完成了, 但是作为一个优秀的你想到了避免阻塞主线程是不是可以用 Worker 起一个新的线程处理,来提升页面性能

Web Worker

1. 作用

  1. 避免主线程阻塞:将耗时任务(如复杂计算、大数据处理)移至后台线程,防止页面卡顿或无响应。
  2. 提升计算性能:利用多核 CPU 并行处理任务(如加密解密、图像/视频处理)。
  3. 异步处理 I/O 操作 :后台处理文件读写(通过 File API)、网络请求(如大文件分片上传)。
  4. 实时数据处理:持续处理 WebSocket 推送的实时数据流(如金融行情、聊天消息)。
  5. 离线任务支持:结合 Service Worker 实现后台同步或离线缓存更新

2. Web Workder 创建方式

  1. 常规创建 Worker
javascript 复制代码
// worker.js
self.onmessage = (e) => {
  // 模拟处理任务
  const result = e.data * 100
  // 返回结果
  self.postMessage(result)
}
js 复制代码
// 主线程
const worker = new Worker('worker.js')  // 指定 Worker 脚本路径
worker.postMessage({ data: 1 })  // 发送消息
// 接收消息
worker.onmessage = (e) => {
  console.log('结果:', e.data)
}
// 手动终止 Worker
worker.terminate()
  1. 动态创建 Worker(通过 Blob/URL)
js 复制代码
// 主线程
const code = `
  self.onmessage = (e) => {
    const result = e.data * 100
    self.postMessage(result)
  };
`
const blob = new Blob([code], { type: 'application/javascript' })
const url = URL.createObjectURL(blob)
const worker = new Worker(url)
worker.postMessage({ data: 1 })  // 发送消息
// 接收消息
worker.onmessage = (e) => {
  console.log('结果:', e.data)
}
// 手动终止 Worker
worker.terminate()

通过Web Worker 实现版本检测

ts 复制代码
// CheckUpdates/utils.ts

// 创建worker
export const createWorker = (func: () => void, deps?: Array<() => void>) => {  
// work 依赖 fns  
const depsFncStr = `${deps?.map(dep => dep.toString()).join(';\n\n') || ''}`  
const blob = new Blob(  
[`  
${depsFncStr};  
(${func.toString()})();  
`],  
{
    type: 'application/javascript'  
})  
return new Worker(window.URL.createObjectURL(blob))  
}

// 动态创建的worker
export const createWorkFn = () => {  
const opts = {  
    immediate: true,  
    intervalTime: 5000, // 毫秒  
    fetchUrl: '/'  
}
let timer: any = null  
const stop = () => {  
    clearInterval(timer)  
}  
const temp: Worker = self as any  
let lastVersionTag = ''  

const getVersionTag = async () => {  
    return fetch(opts.fetchUrl, { method: 'HEAD', cache: 'no-cache' })  
    .then(res => res.headers.get('etag') || res.headers.get('last-modified'))  
    .catch(e => {  
    console.error('Failed to fetch version tag', e)  
    return null  
    })  
}
const doFetch = async () => {  
    const versionTag = await getVersionTag()  
    if (!versionTag) return  
    // 首次运行时不提示更新  
    if (!lastVersionTag) {  
        lastVersionTag = versionTag  
        return  
    }
    if (lastVersionTag !== versionTag) {  
        stop()  
        temp.postMessage({ type: 'showNotice', data: versionTag })  
    }
}

temp.addEventListener('message', async (event: any) => {  
    // { type: 'start' | 'stop' }  
    switch (event.data.type) {  
    case 'start':  
    {  
        const data = event.data.data  
        if (data.intervalTime) opts.intervalTime = data.intervalTime  
        if (data.fetchUrl) opts.fetchUrl = data.fetchUrl  
        if (data.immediate) {  
        await doFetch()  
        }  
        if (timer) stop()  
        timer = setInterval(doFetch, opts.intervalTime)  
    }
    break
    case 'stop': {  
        stop()    
        }
    break
    }  
})
return temp  
}
ts 复制代码
// CheckUpdates
import { createWorker, createWorkFn } from './utils'
const opts = {  
intervalTime: 5 * 60 * 1000, // 5min  
fetchUrl: entrance  
// immediate: false  
}
const worker = createWorker(createWorkFn, [])  
worker.addEventListener('message', (e: any) => {  
 // e.data: {type: 'showNotice', data: 'version'}
 handleNotice()
})  
const start = (immediate = false) => {  
    worker.postMessage({ type: 'start', data: { ...opts, immediate } })  
}  
const stop = () => {  
    worker.postMessage({ type: 'stop' })  
}
const handleNotice = () => {
 console.log('项目更新了, 是时候刷新页面了!!!!')
 // todo 提示用户项目更新操作 +++
}

function handleVisibilitychange() {  
    if (document.hidden) {  
        stop()  
    } else {  
        start(true)  
    }  
}

start()  
document.addEventListener('visibilitychange', handleVisibilitychange)

以上就是完整的检测版本更新的逻辑,完整的项目为 项目链接

文件地址: src/layout/components/CheckUpdates

该项目是 Vue3 + Ts + Element-plus & Vite 搭建,兼容了手机端 开箱即用的后台管理项目 如果觉得不错希望能得到各位大佬的star

访问链接

相关推荐
嘉琪coder4 分钟前
显示器报废,win笔记本远程连接mac mini4 3种方法实测
前端·windows·mac
hrrrrb32 分钟前
【CSS3】筑基篇
前端·css·css3
boy快快长大34 分钟前
【VUE】day01-vue基本使用、调试工具、指令与过滤器
前端·javascript·vue.js
三原39 分钟前
五年使用vue2、vue3经验,我直接上手react
前端·javascript·react.js
嘉琪coder44 分钟前
React的两种状态哲学:受控与非受控模式
前端·react.js
木胭脂沾染了灰1 小时前
策略设计模式-下单
java·前端·设计模式
Eric_见嘉1 小时前
当敦煌壁画遇上 VS Code:我用古风色系开发了编程主题
前端·产品·visual studio code
拉不动的猪1 小时前
刷刷题28(http)
前端·javascript·面试