编程 如何在单页应用(SPA)中实现版本更新检测机制

2024-11-18 21:33:53 +0800 CST views 1008

纯前端怎么实现检测版本更新,请看这篇!


单页应用(Single Page Application,简称SPA)是一种现代Web应用程序架构,通过动态重载页面中的部分内容来提供更流畅和更响应式的用户体验。由于SPA在客户端(用户的浏览器)运行大量的JavaScript代码,并且与传统的多页应用不同,它不会每次操作都从服务器重新加载整个页面内容,因此在性能和用户体验上有显著优势。然而,这种应用也存在一定弊端。譬如,当服务端更新时(接口请求体和响应体结构发生变化),用户所使用的网页静态资源没有同步更新,可能导致报错。


前言

在传统的多页Web应用中,每次用户访问页面时,都会从服务器获取最新的页面和资源,因此版本更新相对简单,用户总是能获取到最新的版本。然而,SPA在首次加载后,前端的静态资源会缓存在浏览器内存中,并且在整个使用过程中通常不会自动重新加载。这种特性意味着如果应用有新的版本发布,用户可能仍在使用旧版本,无法立即获得最新的功能、修复或安全更新。


怎么实现?

我们想实现这样一个效果,场景是:

当工程师发版完成后,客户端检测到有版本更新,给用户一个更新提示,让用户知道有新版本发布。

先来实现这个弹窗:

import { Modal } from 'antd';

function updateNotice() {
    Modal.confirm({
        title: '更新提示',
        content: '检测到新版本,建议立即更新以确保平台正常使用。',
        okText: '确认更新',
        cancelText: '稍后更新',
        onOk: () => {
            window.location.reload();
        },
    });
}

方案一:比较构建文件的hash值

此方案需要webpack开启打包文件带上hash值,具体配置不在此处展开。

通过定期获取服务器的前端资源,匹配资源中的 <script> 标签,对比前后标签是否一致,来检测是否有新的版本发布。

// 存储当前脚本标签的哈希值集合
let scriptHashes = new Set();
let timer = undefined;

/**
 * 从首页获取脚本标签的哈希值集合
 * @returns {Promise<Set<string>>} 返回包含脚本标签的哈希值的集合
 */
async function fetchScriptHashes() {
    // 获取首页HTML内容
    const html = await fetch('/').then((res) => res.text());
    // 正则表达式匹配所有<script>标签
    const scriptRegex = /<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/gi;
    // 获取匹配到的所有<script>标签内容
    const scripts = html.match(scriptRegex) ?? [];
    // 将脚本标签内容存入集合并返回
    return new Set(scripts);
}

/**
 * 比较当前脚本标签的哈希值集合与新获取的集合,检测是否有更新
 */
async function compareScriptHashes() {
    // 获取新的脚本标签哈希值集合
    const newScriptHashes = await fetchScriptHashes();

    if (scriptHashes.size === 0) {
        // 初次运行时,存储当前脚本标签哈希值
        scriptHashes = newScriptHashes;
    } else if (
        scriptHashes.size !== newScriptHashes.size ||
        ![...scriptHashes].every((hash) => newScriptHashes.has(hash))
    ) {
        // 如果脚本标签数量或内容发生变化,则认为有更新
        console.info('更新了', {
            oldScript: [...scriptHashes],
            newScript: [...newScriptHashes],
        });
        // 清除定时器
        clearInterval(timer);
        // 提示用户更新
        updateNotice();
    } else {
        // 没有更新
        console.info('没更新', {
            oldScript: [...scriptHashes],
        });
    }
}

// 每60秒检查一次是否有新的脚本标签更新
timer = setInterval(compareScriptHashes, 60000);

方案二:利用HTTP协议的缓存机制,比较Etag或last-modified前后是否一致

本方案与方案一类似,但更直接地利用了 HTTP 协议提供的缓存控制机制,以确定页面是否发生了变化。

let versionTag = null; // 版本标识
let timer = undefined;

/**
 * 获取首页的 ETag 或 Last-Modified 值,作为当前版本标识
 * @returns {Promise<string|null>} 返回 ETag 或 Last-Modified 值
 */
const getVersionTag = async () => {
    const response = await fetch('/', {
        cache: 'no-cache',
    });
    return response.headers.get('etag') || response.headers.get('last-modified');
};

/**
 * 比较当前的 ETag 或 Last-Modified 值与最新获取的值
 */
const compareTag = async () => {
    const newVersionTag = await getVersionTag();

    if (versionTag === null) {
        // 初次运行时,存储当前的 ETag 或 Last-Modified 值
        versionTag = newVersionTag;
    } else if (versionTag !== newVersionTag) {
        // 如果 ETag 或 Last-Modified 发生变化,则认为有更新
        console.info('更新了', {
            oldVersionTag: versionTag,
            newVersionTag: newVersionTag,
        });
        // 清除定时器
        clearInterval(timer);
        // 提示用户更新
        updateNotice();
    } else {
        // 没有更新
        console.info('没更新', {
            oldVersionTag: versionTag,
            newVersionTag: newVersionTag,
        });
    }
};

// 每60秒检查一次是否有新的 ETag 或 Last-Modified 值
timer = setInterval(compareTag, 60000);

总结

为了确保用户始终使用最新的版本并体验到最佳的功能和安全性,SPA应用需要实现版本检测和更新提示机制。当应用有新版本发布时,可以提示用户刷新页面或自动重新加载,以确保用户获取到最新的应用代码和资源。

推荐文章

Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
PHP 的生成器,用过的都说好!
2024-11-18 04:43:02 +0800 CST
18个实用的 JavaScript 函数
2024-11-17 18:10:35 +0800 CST
聚合支付管理系统
2025-07-23 13:33:30 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
File 和 Blob 的区别
2024-11-18 23:11:46 +0800 CST
Elasticsearch 监控和警报
2024-11-19 10:02:29 +0800 CST
微信内弹出提示外部浏览器打开
2024-11-18 19:26:44 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
PHP openssl 生成公私钥匙
2024-11-17 05:00:37 +0800 CST
LLM驱动的强大网络爬虫工具
2024-11-19 07:37:07 +0800 CST
Go 如何做好缓存
2024-11-18 13:33:37 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
H5保险购买与投诉意见
2024-11-19 03:48:35 +0800 CST
PHP解决XSS攻击
2024-11-19 02:17:37 +0800 CST
CSS 实现金额数字滚动效果
2024-11-19 09:17:15 +0800 CST
如何实现虚拟滚动
2024-11-18 20:50:47 +0800 CST
Go 接口:从入门到精通
2024-11-18 07:10:00 +0800 CST
Nginx rewrite 的用法
2024-11-18 22:59:02 +0800 CST
网络数据抓取神器 Pipet
2024-11-19 05:43:20 +0800 CST
程序员茄子在线接单