English | 简体中文
检测网页更新并通知用户刷新,支持 vite、umijs 和 webpack 插件。
**什么时候会检测更新(fetch version.json)** ?以 git commit hash (也支持 svn revision number、package.json version、build timestamp、custom) 为版本号,打包时将版本号写入 json 文件。客户端轮询服务器上的版本号(浏览器窗口的 visibilitychange、focus 事件辅助),和本地作比较,如果不相同则通知用户刷新页面。
- 首次加载页面。
- 轮询 (default: 10 * 60 * 1000 ms)。
- script 脚本资源加载失败 (404 ?)。
- 标签页 refocus or revisible。
部分用户(老板)没有关闭网页的习惯,在网页有新版本更新或问题修复时,用户继续使用旧的版本,影响用户体验和后端数据准确性。也有可能会出现报错(文件404)、白屏的情况。
# vite
pnpm add @plugin-web-update-notification/vite -D
# umijs
pnpm add @plugin-web-update-notification/umijs -D
# webpack plugin
pnpm add @plugin-web-update-notification/webpack -D
如果 index.html
存在缓存,可能刷新后,更新提示还会存在,所以需要禁用 index.html
的缓存。这也是 SPA
应用部署的一个最佳实践吧。
通过 nginx
,禁用缓存:
# nginx.conf
location / {
index index.html index.htm;
if ( $uri = '/index.html' ) { # disabled index.html cache
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
try_files $uri $uri/ /index.html;
}
直接通过 html meta
标签禁用缓存:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
</head>
</html>
基础使用
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { webUpdateNotice } from '@plugin-web-update-notification/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
logVersion: true,
}),
]
})
自定义通知栏文本
// vite.config.ts
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
notificationProps: {
title: '标题',
description: 'System update, please refresh the page',
buttonText: '刷新',
dismissButtonText: '忽略'
},
}),
]
})
国际化
// vite.config.ts
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
// plugin preset: zh_CN | zh_TW | en_US
locale: "en_US",
localeData: {
en_US: {
title: "📢 system update",
description: "System update, please refresh the page",
buttonText: "refresh",
dismissButtonText: "dismiss",
},
zh_CN: {
...
},
...
},
}),
],
});
// other file to set locale
window.pluginWebUpdateNotice_.setLocale('zh_CN')
取消默认的通知栏,监听更新事件自定义行为
// vite.config.ts
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
hiddenDefaultNotification: true
}),
]
})
// 在其他文件中监听自定义更新事件
document.body.addEventListener('plugin_web_update_notice', (e) => {
const { version, options } = e.detail
// write some code, show your custom notification and etc.
alert('System update!')
})
不支持 umi2
, umi2
可以尝试下通过 chainWebpack
配置 webpack
插件。
// .umirc.ts
import { defineConfig } from 'umi'
import type { Options as WebUpdateNotificationOptions } from '@plugin-web-update-notification/umijs'
export default {
plugins: ['@plugin-web-update-notification/umijs'],
webUpdateNotification: {
logVersion: true,
checkInterval: 0.5 * 60 * 1000,
notificationProps: {
title: 'system update',
description: 'System update, please refresh the page',
buttonText: 'refresh',
dismissButtonText: 'dismiss',
},
} as WebUpdateNotificationOptions
}
// vue.config.js(vue-cli project)
const { WebUpdateNotificationPlugin } = require('@plugin-web-update-notification/webpack')
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
// ...other config
configureWebpack: {
plugins: [
new WebUpdateNotificationPlugin({
logVersion: true,
}),
],
},
})
function webUpdateNotice(options?: Options): Plugin
export interface Options {
/**
* support 'git_commit_hash' | 'svn_revision_number' | 'pkg_version' | 'build_timestamp' | 'custom'
* * if repository type is 'Git', default is 'git_commit_hash'
* * if repository type is 'SVN', default is 'svn_revision_number'
* * if repository type is 'unknown', default is 'build_timestamp'
* */
versionType?: VersionType
/**
* custom version, if versionType is 'custom', this option is required
*/
customVersion?: string
/** polling interval(ms)
* if set to 0, it will not polling
* @default 10 * 60 * 1000
*/
checkInterval?: number
/**
* check update when window focus
* @default true
*/
checkOnWindowFocus?: boolean
/**
* check update immediately after page loaded
* @default true
*/
checkImmediately?: boolean
/**
* check update when load js file error
* @default true
*/
checkOnLoadFileError?: boolean
/**
* whether to output version in console
*
* you can also pass a function to handle the version
* ```ts
* logVersion: (version) => {
* console.log(`version: %c${version}`, 'color: #1890ff') // this is the default behavior
* }
* ```
* @default true
*/
logVersion?: boolean | ((version: string) => void)
/**
* whether to silence the notification.
* such as when local version is v1.0, you can set this option to true and build a new version v1.0.1, then the notification will not show
*/
silence?: boolean
/**
* @deprecated
*/
customNotificationHTML?: string
/** notificationProps have higher priority than locale */
notificationProps?: NotificationProps
notificationConfig?: NotificationConfig
/**
* preset: zh_CN | zh_TW | en_US
* @default 'zh_CN'
* */
locale?: string
/**
* custom locale data
* @link default data: https://github.com/GreatAuk/plugin-web-update-notification/blob/master/packages/core/src/locale.ts
*/
localeData?: LocaleData
/**
* Whether to hide the default notification, if you set it to true, you need to custom behavior by yourself
* ```ts
document.body.addEventListener('plugin_web_update_notice', (e) => {
const { version, options } = e.detail
// write some code, show your custom notification and etc.
alert('System update!')
})
* ```
* @default false
*/
hiddenDefaultNotification?: boolean
/**
* Whether to hide the dismiss button
* @default false
*/
hiddenDismissButton?: boolean
/**
* After version 1.2.0, you not need to set this option, it will be automatically detected from the base of vite config、publicPath of webpack config or publicPath of umi config
*
* Base public path for inject file, Valid values include:
* * Absolute URL pathname, e.g. /foo/
* * Full URL, e.g. https://foo.com/
* * Empty string(default) or ./
*
* !!! Don't forget / at the end of the path
*/
injectFileBase?: string
}
export type VersionType = 'git_commit_hash' | 'pkg_version' | 'build_timestamp' | 'custom'
export interface NotificationConfig {
/**
* refresh button color
* @default '#1677ff'
*/
primaryColor?: string
/**
* dismiss button color
* @default 'rgba(0,0,0,.25)'
*/
secondaryColor?: string
/** @default 'bottomRight' */
placement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
}
export interface NotificationProps {
title?: string
description?: string
/** refresh button text */
buttonText?: string
/** dismiss button text */
dismissButtonText?: string
}
export type LocaleData = Record<string, NotificationProps>
name | params | describe |
---|---|---|
window.pluginWebUpdateNotice_.setLocale | locale(preset: zh_CN、zh_TW、en_US) | set locale |
window.pluginWebUpdateNotice_.closeNotification | close notification | |
window.pluginWebUpdateNotice_.dismissUpdate | dismiss current update and close notification,same behavior as dismiss button | |
window.pluginWebUpdateNotice_.checkUpdate | manual check update, a function wrap by debounce(5000ms) |
interface Window {
pluginWebUpdateNotice_: {
/**
* set language.
* preset: zh_CN、zh_TW、en_US
*/
setLocale: (locale: string) => void
/**
* manual check update, a function wrap by debounce(5000ms)
*/
checkUpdate: () => void
/** dismiss current update and close notification, same behavior as dismiss the button */
dismissUpdate: () => void
/** close notification */
closeNotification: () => void
/**
* refresh button click event, if you set it, it will cover the default event (location.reload())
*/
onClickRefresh?: (version: string) => void
/**
* dismiss button click event, if you set it, it will cover the default event (dismissUpdate())
*/
onClickDismiss?: (version: string) => void
}
}
-
TypeScript
的智能提示, 如果你想使用window.pluginWebUpdateNotice_.
或监听自定义更新事件。// src/shim.d.ts // if you use vite plugin /// <reference types="@plugin-web-update-notification/vite" /> // if you use umi plugin /// <reference types="@plugin-web-update-notification/umijs" /> // if you use webpack plugin /// <reference types="@plugin-web-update-notification/webpack" />
-
请求
version.json
文件提示404 error
。上传打包内容到 cdn 服务器:
// vite.config.ts const prod = process.env.NODE_ENV === 'production' const cdnServerUrl = 'https://foo.com/' export default defineConfig({ base: prod ? cdnServerUrl : '/', plugins: [ vue(), webUpdateNotice({ injectFileBase: cdnServerUrl }) ] })
在非根目录下部署的项目:
// vite.config.ts const prod = process.env.NODE_ENV === 'production' const base = '/folder/' // https://example.com/folder/ export default defineConfig({ base, plugins: [ vue(), webUpdateNotice({ injectFileBase: base }) ] })
After version 1.2.0, you not need to set this option, it will be automatically detected from the base of vite config、publicPath of webpack config or publicPath of umi config
-
自定义
notification
的刷新和忽略按钮事件。// refresh button click event, if you set it, it will cover the default event (location.reload()) window.pluginWebUpdateNotice_.onClickRefresh = (version) => { alert(`click refresh btn: ${version}`) } // dismiss button click event, if you set it, it will cover the default event (dismissUpdate()) window.pluginWebUpdateNotice_.onClickDismiss = (version) => { alert(`click dismiss btn: ${version}`) }
-
自定义 notification 样式。
你可以通过更高的权重覆盖默认样式。(default css file)
<!-- notification html content --> <div class="plugin-web-update-notice-anchor"> <div class="plugin-web-update-notice"> <div class="plugin-web-update-notice-content" data-cy="notification-content"> <div class="plugin-web-update-notice-content-title"> 📢 system update </div> <div class="plugin-web-update-notice-content-desc"> System update, please refresh the page </div> <div class="plugin-web-update-notice-tools"> <a class="plugin-web-update-notice-btn plugin-web-update-notice-dismiss-btn">dismiss</a> <a class="plugin-web-update-notice-btn plugin-web-update-notice-refresh-btn"> refresh </a> </div> </div> </div> </div>
-
手动检测更新
// vue-router check update before each route change router.beforeEach((to, from, next) => { window.pluginWebUpdateNotice_.checkUpdate() next() })
-
部分版本不通知。如客户版本是
v1.0
, 你需要更新v1.0.1
, 但不想显示更新提示。webUpdateNotice({ ... silence: true })