最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端實(shí)現(xiàn)自動(dòng)檢測(cè)更新的三種方式

 更新時(shí)間:2025年06月25日 08:48:58   作者:程序員大衛(wèi)  
對(duì)于一些基于SPA構(gòu)建的ToC網(wǎng)站,或者嵌入 App 的 Hybrid H5 頁(yè)面,在項(xiàng)目發(fā)布上線后,我們希望能夠自動(dòng)檢測(cè)是否有新版上線并及時(shí)提示用戶刷新頁(yè)面,所以本文給大家介紹了前端實(shí)現(xiàn)自動(dòng)檢測(cè)更新的三種方式,需要的朋友可以參考下

前言

對(duì)于一些基于 SPA 構(gòu)建的 ToC 網(wǎng)站,或者嵌入 App 的 Hybrid H5 頁(yè)面,在項(xiàng)目發(fā)布上線后,我們希望能夠自動(dòng)檢測(cè)是否有新版上線并及時(shí)提示用戶刷新頁(yè)面,避免用戶繼續(xù)使用緩存的老版本頁(yè)面。

核心思路就是:周期性地對(duì)比“版本標(biāo)識(shí)”是否發(fā)生變化,如果變化了就提示用戶刷新。本文介紹三種用于生成和獲取“版本標(biāo)識(shí)”的方法。

這些方法都采用輪詢機(jī)制檢測(cè)更新,只是獲取“標(biāo)識(shí)”的方式不同。

通用檢測(cè)邏輯

const LOOP_TIME = 4000;
const loopCheckFlag = async () => {
  const preFlag = await getFlag(); // 獲取標(biāo)識(shí)
  const loop = async () => {
    const curFlag = await getFlag();
    if (preFlag && curFlag && curFlag !== preFlag) {
      alert("檢測(cè)到有版本更新");
      return;
    }
    setTimeout(loop, LOOP_TIME);
  };
  loop();
};

loopCheckFlag();

你只需要替換上面的 getFlag() 方法即可切換不同實(shí)現(xiàn)方式。

方法一:對(duì)比前后 ETag 或 Last-Modified

這是最簡(jiǎn)單的一種方式,通過(guò)對(duì)比頁(yè)面響應(yīng)頭中的 EtagLast-Modified 字段判斷頁(yè)面是否更新。

async function getFlag() {
  const response = await fetch('/', {
    method: "HEAD",
    cache: "no-cache",
  });
  return response.headers.get("Etag") || response.headers.get("last-modified");
}

注意:

  • 這種方式依賴后端是否返回 Etaglast-modified;
  • 如果這兩個(gè)值都為 null,則無(wú)法檢測(cè)更新,建議加以判斷處理。

方法二:構(gòu)建時(shí)生成 HTML 的 MD5 文件

由于每次發(fā)布后 index.html 的內(nèi)容都會(huì)發(fā)生變化(比如引用的入口 JS 文件名變化),我們可以在構(gòu)建完成后,生成 HTML 文件的 MD5 值,然后通過(guò)對(duì)比是否變化來(lái)判斷是否有新版本。

獲取方式

async function getFlag() {
  const response = await fetch('/htmlVersion.text', {
    method: "GET",
    cache: "no-cache",
  });
  return response.text();
}

Node 側(cè)生成 md5 的工具函數(shù)

// plugins/common/util.ts
import fs from 'fs/promises';
import crypto from 'crypto';

export function getMd5(filePath: string): Promise<string> {
  return fs.readFile(filePath).then((data) =>
    crypto.createHash('md5').update(data).digest('hex')
  );
}

2.1 Vite 插件實(shí)現(xiàn)

// plugins/CreateHtmlVersion/index.ts
import fs from "fs/promises";
import path from "path";
import type { ConfigEnv, Plugin } from "vite";
import { getMd5 } from "../common/util";

export default function createHtmlVersion(configEnv: ConfigEnv): Plugin {
  return {
    name: "vite-plugin-html-md5",
    apply: "build",
    async closeBundle() {
      if (configEnv.mode !== "production") return;

      const outDir = "dist";
      const htmlPath = path.resolve(outDir, "index.html");
      const versionPath = path.resolve(outDir, "htmlVersion.text");

      try {
        const md5 = await getMd5(htmlPath);
        await fs.writeFile(versionPath, md5);
        console.log("? 生成 htmlVersion.text 成功:", md5);
      } catch (e) {
        console.error("? 生成 htmlVersion.text 失敗:", e);
      }
    },
  };
}

應(yīng)用插件:

// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import createHtmlVersion from "./plugins/CreateHtmlVersion";

export default defineConfig((configEnv) => ({
  plugins: [
    vue(),
    createHtmlVersion(configEnv),
  ],
}));

2.2 Webpack 插件實(shí)現(xiàn)

// plugins/CreateHtmlVersion/index.ts
import path from 'path';
import fs from 'fs/promises';
import { getMd5 } from "../common/util";

export default class CreateHtmlVersion {
  private env: string;

  constructor(env: string) {
    this.env = env;
  }

  apply(compiler) {
    if (this.env === "local") return;

    compiler.hooks.done.tapPromise("CreateHtmlVersion", async (stats) => {
      try {
        const outputPath = stats.compilation.outputOptions.path;
        const htmlFilePath = path.join(outputPath, "index.html");
        const versionFilePath = path.join(outputPath, "htmlVersion.text");

        const md5 = await getMd5(htmlFilePath);
        await fs.writeFile(versionFilePath, md5);

        console.log('? 生成 htmlVersion.text 成功:', md5);
      } catch (e) {
        console.error('? 生成 htmlVersion.text 失敗:', e);
      }
    });
  }
}

craco.config.js 中引入并應(yīng)用插件:

import CreateHtmlVersion from "./plugins/CreateHtmlVersion";

export default {
  webpack: {
    plugins: [
      new CreateHtmlVersion(process.env.REACT_APP_ENV)
    ],
  }
};

并在根目錄配置環(huán)境變量:

# .env.local
REACT_APP_ENV=local

注: 由于這個(gè)項(xiàng)目比較老,是基于 craco,所以項(xiàng)目的根目錄還需新建 .env.local 文件。

方法三:直接對(duì)比 HTML 內(nèi)容或 JS 文件路徑變化

這是最“直接粗暴”的方式 —— 每次輪詢時(shí)獲取 /index.html 的完整文本內(nèi)容,然后進(jìn)行對(duì)比。

async function getFlag() {
  const response = await fetch('/', {
    method: "GET",
    cache: "no-cache",
  });
  return response.text();
}

該方式簡(jiǎn)單易實(shí)現(xiàn),但對(duì)比的是整份 HTML 內(nèi)容,若只是靜態(tài)資源變更,也可能導(dǎo)致不必要的觸發(fā)。

進(jìn)一步優(yōu)化:對(duì)比 JS 文件路徑是否變化

為了更精準(zhǔn)地檢測(cè)版本變更,可以從 HTML 中提取出所有 <script src="..."> 標(biāo)簽中的 JS 地址,并將它們組成一個(gè)標(biāo)識(shí)進(jìn)行對(duì)比。

當(dāng)構(gòu)建后的 JS 文件帶有哈希(如 /assets/index.abc123.js),只要有資源更新,路徑就會(huì)變化。相比對(duì)比整個(gè) HTML 文本,這種方式更加高效、精準(zhǔn)。

getFlag 實(shí)現(xiàn)

/**
 * 用于輪詢檢測(cè)是否有 JS 文件變化
 */
async function getFlag() {
  const html = await fetchIndexHtml();
  const jsUrls = extractJsUrls(html);
  return jsUrls.join(',');
}

工具函數(shù)

/**
 * 請(qǐng)求 index.html 內(nèi)容
 */
async function fetchIndexHtml() {
  const response = await fetch('/', {
    method: 'GET',
    cache: 'no-cache',
  });
  return response.text();
}

// 正則:匹配所有 <script src="..."> 的標(biāo)簽
const scriptSrcRegex = /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*><\/script>/gi;

/**
 * 從 HTML 中提取所有 JS 地址
 */
function extractJsUrls(html: string): string[] {
  const result: string[] = [];
  let match: RegExpExecArray | null;

  while ((match = scriptSrcRegex.exec(html)) !== null) {
    result.push(match[1]);
  }

  return result.sort(); // 排序,確保對(duì)比結(jié)果穩(wěn)定
}

總結(jié)

方法是否依賴后端可控性推薦程度
ETag / Last-Modified不可控??
構(gòu)建 MD5 文件對(duì)比可控????
對(duì)比 HTML 內(nèi)容 / JS 內(nèi)容靈活但復(fù)雜???

推薦使用第二種方式(構(gòu)建后生成 MD5 文件),兼容性強(qiáng)、部署后可控性高,配合 Vite 或 Webpack 插件都能靈活實(shí)現(xiàn)。

以上就是前端實(shí)現(xiàn)自動(dòng)檢測(cè)更新的三種方式的詳細(xì)內(nèi)容,更多關(guān)于前端自動(dòng)檢測(cè)更新的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

邹平县| 茌平县| 房产| 崇文区| 精河县| 淅川县| 苏尼特左旗| 枣庄市| 许昌市| 黎城县| 寻乌县| 若尔盖县| 襄城县| 永善县| 浦城县| 石嘴山市| 蕉岭县| 健康| 抚州市| 奇台县| 兴安盟| 西丰县| 宜都市| 威信县| 高陵县| 通榆县| 潍坊市| 定安县| 麻城市| 雷州市| 威信县| 美姑县| 东兴市| 罗田县| 义乌市| 江都市| 和林格尔县| 克拉玛依市| 鲁山县| 富锦市| 塔城市|