页面实现自动刷新对服务器要求 (实现页面滚动刷新)

今天分享一个实现页面自动检测更新,而且不需要动服务端代码的方法,比较取巧,在非覆盖式发布的情况下可以简单使用。

背景和思路

有部分运营是不刷新页面的,当我们前端资源更新了,他可能还在使用老的资源。

目标是希望在我们前端资源更新的时候提示用户有资源更新,然后刷新页面获取最新的前端资源。

一个适合非覆盖式发布的前端应用的自动检测更新方法。

覆盖式发布

前后两次发布的资源没有差异,js 资源也不使用 hash 进行区分

非覆盖式发布

前后两次发布的资源会有差异,比如体现在 js 资源带上 hash 或者发布的版本号

所以, 我们以构建之后 js 资源带 hash 的页面为例,如果有新的发布,那么 js 资源的链接就会发生改变,通过比较前后两次的资源地址来判断页面是否发布。

实现页面自动更新,实现页面滚动刷新

构建之后是带 hash 的资源文件

代码

export const DEYAL_UPDATE_INTERVAL = 30 * 1000; // 30 秒

let initJsUrls: string[] = [];

// 获取 html
export async function fetchHtml(
  url: string,
  fetcher = window.fetch
): Promise<string> {
  try {
    const res = await fetcher(url);
    return res.text();
  } catch (error) {
    console.error(error);
    return "";
  }
}

// 提取 HTML 中所有 js 的地址
export function extractJsUrls(html: string): string[] {
  const regex = /<script.*?src="(.*?)"/g;
  const matches = [...html.matchAll(regex)];
  return matches.map((match) => match[1]);
}

export async function needUpdate(targetUrl: string) {
  const html = await fetchHtml(targetUrl);
  let result = false;
  if (html) {
    const newJsUrls = extractJsUrls(html);
    console.log(initJsUrls, newJsUrls);

    if (initJsUrls.length != newJsUrls.length) {
      result = true;
    }

    for (let i = 0; i < newJsUrls.length; i++) {
      if (newJsUrls[i] !== initJsUrls[i]) {
        result = true;
        break;
      }
    }

    if (!initJsUrls.length) {
      result = false;
    }
    initJsUrls = newJsUrls;
  }

  return result;
}

// 每过一段时间检测一次是否需要更新
export function autoUpdate(
  url: string,
  duration: number = DEYAL_UPDATE_INTERVAL
) {
  setTimeout(async () => {
    const willUpdate = await needUpdate(url);

    if (willUpdate) {
      const result = confirm("检测到新版本,是否更新?");
      if (result) {
        window.location.reload();
      }
    }

    autoUpdate(url, duration);
  }, duration);
}