通知用户刷新这么简单?拿来吧你🤲

1,795 阅读3分钟

前言

测试:我没看见新功能啊,开发同学有问题啊。

我:刷新一下呢?

测试:哦有了有了

我:😡😡😡

不行,这种对话太频繁了,测试、产品、用户、后端甚至老板,时不时就会提出这种问题。痛定思痛,一定得想个办法处理一下。

方案探索

方案1:通知平台

  1. 新增加一个发消息的平台
  2. 后端监听这个消息的数据库
  3. 并且利用 websocket ,在进入页面的时候就进行连接。
  4. 前端专门暴露一个通知框给这个 ws 使用

这样一来,不管后面想通知什么,都可以走这个平台,有很强的可复用性。

后端同学:?再弄个平台再监听再发送,我哪有那么多功夫。

我:😭😭😭

方案2:build 时追加文件

参考了这篇方案 juejin.cn/post/720774…

  1. 在build时,生成一个 _version.js 文件,写入当前时间
const fs = require('fs');

const version = Date.now();
const content = `window._version = '${version}';`;

fs.writeFileSync('./_version.js', content);
  1. 将该文件加入最终生成的 index.html 中

  2. 去轮询 _version.js 文件,观察远程的 window._version 文件是否和本地不一致

这个方案确实是靠前端可以做到的,但是这时候有同学就要问了:

imooimoo,这个方案虽好,但是有点太过复杂了。有没有更简单的方案啊?

有的兄弟有的,在刚才的方案中,有一个点尤为关键,就是轮询 目标文件,如果仔细想想,真的有必要新去建立一个文件然后轮询吗,我们就不能轮询整个前端文件进行对照吗?

举个例子,这是一个网站的所有文件:

由于是 spa 单页面设计,变动基本都在 js 里,我们只需要去看所有 js 文件是否变动就可以了。

甚至可以再进一步,我们只需要看哈希值有没有变化就行,也就是拿到所有文件的名字进行对照即可。

再再进一步,我们甚至可以只请求 html 文件,因为所有的 js 都是通过 script 标签进去的。在这里一定能提取出他们的名字

思路已经有了,开写!

整体实现

  1. 如何请求当前的 html 文件?查阅资料可知
const html = await fetch('/').then((res) => res.text());
  1. 如何解析出 script 标签
const reg = /<script(?:\s+[^>]*)?>(.*?)</script\s*>/gi;
const list: string[] = html.match(reg) || [];

最难的两步已经找到了答案,剩下的就信手拈来了。贴个完整代码:

import { Notification } from '@arco-design/web-react';

export class Updater {
  oldScript: string[] = [];

  intervalId: number | null = null;

  constructor() {
    this.init();
  }

  static async getScriptList() {
    const html = await fetch('/').then((res) => res.text());
    const reg = /<script(?:\s+[^>]*)?>(.*?)</script\s*>/gi;
    const list: string[] = html.match(reg) || [];
    return list;
  }

  static compare(oldArr: string[], newArr: string[]) {
    const isSame =
      oldArr.length === newArr.length &&
      oldArr.every((v, i) => v === newArr[i]);

    if (!isSame) {
      Notification.warning({
        id: 'updater',
        title: '更新提示',
        content: '页面有更新,请刷新页面来获取最新功能',
        duration: 30 * 1000,
      });
    }
  }

  async init() {
    const list: string[] = await Updater.getScriptList();
    this.oldScript = list;
  }

  start() {
    const time = 1000 * 60 * 60; // 周期为 1h
    this.intervalId = window.setInterval(async () => {
      const list = await Updater.getScriptList();
      Updater.compare(this.oldScript, list);
      this.oldScript = list; // 始终更新缓存
    }, time);
  }

  stop() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }
}

非常简单,只需要添加这个文件,并启用功能即可。

const updater = new Updater();
updater.start();

大功告成,完结撒花,给个赞吧~