前端版本检测,提示用户刷新

6 阅读1分钟

vue.config.js配置项

  configureWebpack: config => {
	...
    config.output.filename = `static/js/[name].${Date.now()}.js`
    config.output.chunkFilename = `static/js/buildVersion/[name].${Date.now()}.js`
	...
    return {
      name,
      resolve: {
        alias: {
          '@': resolve('src')
        }
      }
    }
  }

校验静态资源加载错误

function handleListenerError(eventErr) {
  if (eventErr.filename.indexOf('buildVersion') > -1) {
    showNotification()
  }
  eventErr.preventDefault()
}
function listenerError() {
  window.addEventListener('error', debounce(handleListenerError), true)
}

listenerError()

控制台输入以下内容后回车:

const script = document.createElement('script');  
script.type = 'text/javascript';  
script.src = '/static/js/buildVersion/non-existent-file.js'; // 不存在的路径  
document.head.appendChild(script); // 添加到 DOM 中触发加载

version-upgrade.js

import { Notification } from 'element-ui'

const LOOPER_TIME = 1000 * 30 // 检测版本更新的轮训时间
let preTag = '' // 用于记录初次进入网站或网站被刷新时的Etag信息
let timeoutId = null

export const checkForUpdates = async () => {
  const curTag = await getTag()
  console.log('preTag', preTag, timeoutId)
  console.log('curTag', curTag, timeoutId)
  if (preTag !== curTag) {
    showNotification()
    if (timeoutId) {
      clearTimeout(timeoutId)
      timeoutId = null
    }
  } else {
    timeoutId = setTimeout(checkForUpdates, LOOPER_TIME) // 重新设置定时器
  }
}

const init = async () => {
  preTag = await getTag()
  console.log('初始化-preTag', preTag)
  if (timeoutId) {
    clearTimeout(timeoutId)
    timeoutId = null
  }
  // timeoutId = setTimeout(checkForUpdates, LOOPER_TIME) // 初次设置定时器
}

init()

/**
 * 检测到版本更新后,notification会一直显示在页面上
 */
const showNotification = () => {
  Notification.closeAll()
  Notification({
    duration: 0,
    title: '版本更新提示',
    dangerouslyUseHTMLString: true,
    message:
      "检测到有新版本发布,请<a href='javascript:location.reload()' style='color: green'>刷新</a>页面",
  })
}

/**
 * 获取网站资源的Etag信息
 *
 * @returns 网站资源的Etag信息
 */
async function getTag() {
  const response = await fetch(window.location.origin, {
    method: 'HEAD', // 用于获取资源的元数据,与GET请求类似,HEAD请求也像服务器发送请求,但服务器只需要回传资源的头部信息,不需要回传资源的实体主体。
    cache: 'no-cache'
  })
  return response.headers.get('Etag')
}

// 监听错误
const debounce = (fn, delay = 200) => {
  let timer = null
  return (...args) => {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn(...args)
    }, delay)
  }
}

function handleListenerError(eventErr) {
  console.log('监听到静态资源加载错误', eventErr)
  if (eventErr.filename.indexOf('buildVersion') > -1) {
    showNotification()
  }
  eventErr.preventDefault()
}
function listenerError() {
  window.addEventListener('error', debounce(handleListenerError), true)
}

listenerError()


main.js中导入

import './version-upgrade' // 发布版本提示用户刷新

路由切换时触发检测

路由守卫中使用:

import { checkForUpdates } from './utils/version-upgrade'

...

router.afterEach(() => {  
  checkForUpdates()  
})