SPA项目前端重新部署如何通知用户刷新网页

282 阅读1分钟

📌 问题背景

SPA项目采用客户端渲染,页面初始化和渲染在浏览器中完成。用户不刷新页面,仍使用旧的缓存代码。如果项目重新部署,用户感知不到更新。

💡 解决思路

可以通过在路由钩子中添加版本检测代码,比较打包后script文件名的hash,来判断是否有新的部署版本。

如果检测到版本变化,可以在页面中弹出提示,要求用户手动刷新页面,加载最新的代码。

🛠 核心代码

export class Updater {
  oldScript: string[] // 存储第一次值也就是script 的hash 信息
  newScript: string[] // 获取新的值 也就是新的script 的hash信息

  constructor() {
    this.oldScript = []
    this.newScript = []
    this.init() // 初始化
  }

  async init() {
    const html: string = await this.getHtml()
    this.oldScript = this.parserScript(html)
  }

  async getHtml() {
    try {
      const html = await fetch(`/?_timestamp=${Date.now()}`).then(res => res.text()) // 读取index html
      return html
    }
    catch (error) {
      console.error('Error occurred while fetching HTML:', error)
      throw error
    }
  }

  parserScript(html: string) {
    const reg = /<script(?:\s+[^>]*)?>(.*?)</script\s*>/ig // script正则
    const scriptArr = html.match(reg) as string[] // 匹配script标签
    return scriptArr
  }

  findVersion(arr: string[]) {
    const result = arr.find(script => /index/i.test(script))
    if (result) {
      const regex = /index-(.*?).js/
      const match = result.match(regex)
      if (match && match.length > 1) {
        const number = match[1]
        return { number }
      }
    }
  }

  async checkUpdate() {
    try {
      const newHtml = await this.getHtml()
      const scriptArr = this.parserScript(newHtml)
      const number = this.findVersion(scriptArr)
      return number
    }
    catch (error) {
      console.error('Error occurred during update check:', error)
    }
  }
}

🚀 示例

// 创建实例
const updater = new Updater() 

router.afterEach((to) => {
  updater.checkUpdate().then((result: any) => {
    console.log(result)
    const version = sessionStorage.getItem('version')
    const newVersion = result.number
    if (!newVersion) return
    if (!version) {
      sessionStorage.setItem('version', newVersion)
      return
    }
    if (version !== newVersion) {
      updateModal()
      sessionStorage.setItem('version', newVersion)
    }
  })
})

const updateModal = () => {
  const key = `open${Date.now()}`
  notification.config({
    maxCount: 1,
  })
  notification.open({
    duration: null,
    icon: () => h(ExclamationCircleOutlined, { style: 'color: #ffa900' }),
    message: '发现新版本',
    description: '检测到到最新版本,刷新后立即使用',
    btn: () => h(Button, {
      type: 'primary',
      size: 'small',
      onClick: () => window.location.reload(),
    }, {
      default: () => '立即刷新',
    }),
    key,
    onClose: () => {},
  })
}