English | 简体中文
plugin-web-update-notification
Detect webpage updates and notify user to reload. support vite, umijs and webpack.
Take the git commit hash (also support svn revision number、package.json version、build timestamp、custom) as the version number, and write version into json file. The client polls the version of the server (visibilitychange or focus event assistant), compares it with the local one, and if it is not the same, notifies the user to refresh the page (you can custom behavior).
When to check for updates (fetch version.json) ?
- first load page.
- poll (default: 10 * 60 * 1000 ms).
- script resource loading failure detected (404 ?).
- when the tab page is refocus or revisible.
Why
Some users do not have the habit of closing web pages. If the front-end page is updated, the user page has always been a historical version, any there may be report an error (file 404) or a white screen.
Install
pnpm add @plugin-web-update-notification/vite -D
pnpm add @plugin-web-update-notification/umijs -D
pnpm add @plugin-web-update-notification/webpack -D
Usage
vite | umi | webpack
Important: Disable index.html
caching!
If index.html
is cached, the update notification may still appear after refreshing, so it is necessary to disable the caching of index.html
. This is also a best practice for deploy SPA applications.
To disable caching through 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;
}
Directly disable caching through html meta
tags:
<!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
basic usage
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { webUpdateNotice } from '@plugin-web-update-notification/vite'
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
logVersion: true,
}),
]
})
custom notification text
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
notificationProps: {
title: 'system update',
description: 'System update, please refresh the page',
buttonText: 'refresh',
},
}),
]
})
internationalization
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
locale: "en_US",
localeData: {
en_US: {
title: "📢 system update",
description: "System update, please refresh the page",
buttonText: "refresh",
dismissButtonText: "dismiss",
},
zh_CN: {
...
},
...
},
}),
],
});
window.pluginWebUpdateNotice_.setLocale('zh_CN')
hidden default notification, listener to update event and custom behavior.
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
hiddenDefaultNotification: true
}),
]
})
document.body.addEventListener('plugin_web_update_notice', (e) => {
const { version, options } = e.detail
alert('System update!')
})
Umijs
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
}
webpack
const { WebUpdateNotificationPlugin } = require('@plugin-web-update-notification/webpack')
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
configureWebpack: {
plugins: [
new WebUpdateNotificationPlugin({
logVersion: true,
}),
],
},
})
suggest: disabled index.html cache
# 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;
}
webUpdateNotice Options
function webUpdateNotice(options?: Options): Plugin
export interface Options {
versionType?: VersionType
customVersion?: string
checkInterval?: number
checkOnWindowFocus?: boolean
checkImmediately?: boolean
checkOnLoadFileError?: boolean
logVersion?: boolean | ((version: string) => void)
silence?: boolean
customNotificationHTML?: string
notificationProps?: NotificationProps
notificationConfig?: NotificationConfig
locale?: string
localeData?: LocaleData
hiddenDefaultNotification?: boolean
hiddenDismissButton?: boolean
injectFileBase?: string
}
export type VersionType = 'git_commit_hash' | 'pkg_version' | 'build_timestamp' | 'custom'
export interface NotificationConfig {
primaryColor?: string
secondaryColor?: string
placement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
}
export interface NotificationProps {
title?: string
description?: string
buttonText?: string
dismissButtonText?: string
}
export type LocaleData = Record<string, NotificationProps>
export Functions
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_: {
setLocale: (locale: string) => void
checkUpdate: () => void
dismissUpdate: () => void
closeNotification: () => void
onClickRefresh?: (version: string) => void
onClickDismiss?: (version: string) => void
}
}
What was changed
Q&A
-
TypeScript
intellisense, if you use window.pluginWebUpdateNotice_.
or listener custom update event。
-
request version.json
file get 404 error
.
If you upload the production files bundled to cdn server:
const prod = process.env.NODE_ENV === 'production'
const cdnServerUrl = 'https://foo.com/'
export default defineConfig({
base: prod ? cdnServerUrl : '/',
plugins: [
vue(),
webUpdateNotice({
injectFileBase: cdnServerUrl
})
]
})
Deploy the project in a non-root directory:
const prod = process.env.NODE_ENV === 'production'
const base = '/folder/'
export default defineConfig({
base,
plugins: [
vue(),
webUpdateNotice({
injectFileBase: base
})
]
})
After version 1.2.0, in most case, you not need to set injectFileBase, it will be automatically detected from the base of vite config、publicPath of webpack config or publicPath of umi config
-
Custom notification button event.
window.pluginWebUpdateNotice_.onClickRefresh = (version) => { alert(`click refresh btn: ${version}`) }
window.pluginWebUpdateNotice_.onClickDismiss = (version) => { alert(`click dismiss btn: ${version}`) }
-
Custom notification style.
you can cover css styles with higher weights. (default css file)
<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>
-
manual check update.
router.beforeEach((to, from, next) => {
window.pluginWebUpdateNotice_.checkUpdate()
next()
})
-
Some versions do not notify. For example, if the customer version is v1.0
, you need to update to v1.0.1
, but do not want to display the update prompt.
webUpdateNotice({
...
silence: true
})
License
MIT