背景
最近发现很多手机APP和小程序在打开或者使用过程中会弹框提示用户新版本发布,是否更新。我就想着自己能不能实现一个。
点此跳转Github查看源码
功能要求
- 纯前端实现,不限制框架用React、Vue、Angular都可
- 不限制打包工具Webpack、Vite都可使用
- 使用发布订阅模式实现,支持用户自定义更新提醒事件
- 发布到npm,安装后开箱即用
功能实现
功能实现也非常简单,大致实现如下:
- 打包构建的时候拿到webpack或者vite的打包
id
,通过Node的fs
模块将其放到打包(/dist)目录下(最好是可访问的静态资源里面,例如/public/config.json)。
// /public/config.json{hash:"123456"}
- 我们需要写一个webpack或者vite插件在打包构建完成后拿到hash生成文件,然后输出到/dist目录里,还好我们有unplugin,一套代码可以适配webpack、vite插件。> 注意:Webpack的afterEmit钩子里有提供当前打包构建的hash值,而Vite没有(希望只是我没有找到,有没有大佬告诉我Vite哪个钩子可以获取到打包构建的hash值)所以我是自己生成了uuid作为hash代码如下:
import{ createUnplugin }from"unplugin"import{ writeFileSync, mkdirSync }from"node:fs"import path from"node:path"// 生成config.json文件constgenerateConfig=(configPath: string,hash: string)=>{// 确保目录存在mkdirSync(path.dirname(configPath),{recursive:true})writeFileSync( configPath,JSON.stringify({ hash,},null,2))}// filePath不传默认生成到/dist目录下exportdefaultcreateUnplugin((filePath: string ="config.json")=>{let viteOutDir ="dist"return{name:"unplugin-app-update",// 生成Vite插件vite:{configResolved(config){ viteOutDir = config.build.outDir },closeBundle(){const configPath = path.join(process.cwd(), viteOutDir, filePath)generateConfig(configPath,uuid())},},// 生成webpack插件webpack(compiler){// 只在生产模式下生成文件if(compiler.options.mode !=="production"){return} compiler.hooks.afterEmit.tap("unplugin-app-update",(compilation: any)=>{const configPath = path.join( compiler.options.output.path as string, filePath )generateConfig(configPath, compilation.hash)})},}})
- 然后前端通过轮询这个文件,对比hash有变化则打开一个弹窗提醒用户有新功能发布。 我用
发布订阅模式
写了一个AppUpdate
的类去实现这个功能,。 首先我们需要有一个获取config.json配置文件的方法,为了不引入axios直接使用fetch API
exportclassAppUpdate{// 请求配置文件的url,默认不传请求url: http://your.website.com/config.jsonconstructor({url ="config.json"}){this.url = url
}/* ... */asyncgetConfig(){const config =awaitfetch(this.url,{// 强制开启协商缓存headers:{"Cache-Control":"max-age=0"},}).then((res)=> res.text())returnJSON.parse(config)}/* ... */
- 然后我们需要在首次进入页面时加载一次当前配置文件获取初始hash值
asyncinit(){this.oldConfig =awaitthis.getConfig()}
- 开启轮询,对比hash是否变化
// 开始检查check(){this.stop()this.timer =setInterval(async()=>{this.newConfig =awaitthis.getConfig()this.compare()},this.interval)as unknown as number
}// 停止检查stop(){clearInterval(this.timer)}// 对比compare(){if(this.newConfig.hash ===this.oldConfig.hash){this.dispatch("notUpdate")}else{this.oldConfig =this.newConfig
this.newConfig ={}this.dispatch("update")}}// 触发事件dispatch(key:"notUpdate"|"update"){this.callbacks[key]?.forEach((callback: AnyMethod)=>{callback()})}
- 支持用户自定义更新和未更新的事件回调
on(key:"notUpdate"|"update",callback: AnyMethod){;(this.callbacks[key]||(this.callbacks[key]=[])).push(callback)}off(key:"notUpdate"|"update",callback: AnyMethod){const index =(this.callbacks[key]||(this.callbacks[key]=[])).findIndex((item)=> item === callback
)if(index !==-1){this.callbacks[key].splice(index,1)}}
- 默认添加应用更新弹窗提醒
exportclassAppUpdate{// 初始化constructor({
url ="config.json",
interval =30000,
locate,
custom =false,}){// 国际化,默认为当前浏览器语言if(locate){
i18n.changeLanguage(locate)}this.url = url
this.interval = interval
// 初次获取config文件hash值this.init()// 开始轮询this.check()// 添加默认提醒事件,自定义设置可设置custom: trueif(!custom){this.on("update",()=>{if(!this.modal){this.modal = Modal.confirm({title: i18n.t("updateModelTitle"),content: i18n.t("updateModelContent"),style:{top:200,},okText: i18n.t("comfirm"),cancelText: i18n.t("cancel"),onOk:()=>{
window.location.reload()},onCancel:()=>{this.modal =null},})}})}}/* ... */}
如何使用
安装
// npm
npm i unplugin-app-update -S// pnpm
pnpm i unplugin-app-update -S// yarn
yarn add unplugin-app-update
Webpack
// webpack.config.jsconst appUpdate =require("unplugin-app-update/webpack")
module.exports ={/* ... */plugins:[appUpdate('/path/to/config.json'),],}
Vite
// vite.config.tsimport appUpdate from"unplugin-app-update/vite"exportdefaultdefineConfig({plugins:[// default to the dist directoryappUpdate(),],})
入口配置
// main.js or index.jsimport{ AppUpdate }from"unplugin-app-update"const appUpdate =newAppUpdate({/* Options */})// 停止轮询// appUpdate.stop()// 继续轮询// appUpdate.check()// 事件:update, notUpdateconstupdate=()=>{
console.log("Update")}// 自定义更新用户提醒
appUpdate.on("update", update)
appUpdate.on("notUpdate",()=>{
console.log("Not Update")})// 解绑事件
appUpdate.off("update", update)
AppUpdate选项
属性类型描述默认值urlStringconfig.json配置文件url
config.json
intervalNumber轮询时间间隔
30000
locate
zh_CN
|
en_US
国际化
Default language of browser
customBoolean设置为true可删除默认弹出提醒,添加on(‘update’,fn)事件自定义弹出
false
注意
在本地开发时,需要在公共目录中放置一个config.json文件
手动更改哈希值以模拟项目构建
如果webpack或vite调整了公共目录,您应该新建AppUpdate({url:‘your/customer/path’)
// /public/config.json{hash:"123456"}
404错误
在生产和本地开发过程中,配置时经常遇到404错误。找不到json
// webpack.config.js or vite.config.ts{output:{// 修改打包后访问路径publicPath:"/",}}
最后
- 都看到这了,可以给我的Github仓库点个小小的Star吗?这真的对我很重要!重要!重要!欢迎给我提Issue、共建。
- 有兴趣可以加我微信号:vgbire,一起了解更多前端资讯。
版权归原作者 Vgbire 所有, 如有侵权,请联系我们删除。