前端检测版本更新-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

访问链接

相关推荐
weixin_382395237 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__7 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.8 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~9 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华10 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子12 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅12 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni13 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学14 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端