5

前端重新部署如何通知用户刷新网页?

 1 year ago
source link: https://www.fly63.com/article/detial/12353
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.目标场景

有时候上完线,用户还停留在老的页面,用户不知道网页重新部署了,跳转页面的时候有时候js连接hash变了导致报错跳不过去,并且用户体验不到新功能。

2.思考解决方案

如何去解决这个问题 思考中...

如果后端可以配合我们的话我们可以使用webSocket 跟后端进行实时通讯,前端部署完之后,后端给个通知,前端检测到Message进行提示,还可以在优化一下使用EvnentSource 这个跟socket很像只不过他只能后端往前端推送消息,前端无法给后端发送,我们也不需要给后端发送。

以上方案需要后端配合,奈何公司后端都在忙,需要纯前端实现。

重新进行思考...

根据和小伙伴的讨论得出了一个方案,在项目根目录给个json 文件,写入一个固定的key值然后打包的时候变一下,然后代码中轮询去判断看有没有变化,有就提示。

但是写完之后发现太麻烦了,需要手动配置json文件,还需要打包的时候修改,有没有更简单的方案, 进行第二轮讨论。

第二轮讨论的方案是根据打完包之后生成的script src 的hash值去判断,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了.

63dc63c906d11.jpg

3.代码实现

interface Options {
    timer?: number
}

export class Updater {
    oldScript: string[] //存储第一次值也就是script 的hash 信息
    newScript: string[] //获取新的值 也就是新的script 的hash信息
    dispatch: Record<string, Function[]> //小型发布订阅通知用户更新了
    constructor(options: Options) {
        this.oldScript = [];
        this.newScript = []
        this.dispatch = {}
        this.init() //初始化
        this.timing(options?.timer)//轮询
    }


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

    async getHtml() {
        const html = await fetch('/').then(res => res.text());//读取index html
        return html
    }

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

    //发布订阅通知
    on(key: 'no-update' | 'update', fn: Function) {
        (this.dispatch[key] || (this.dispatch[key] = [])).push(fn)  
        return this;
    }

    compare(oldArr: string[], newArr: string[]) {
        const base = oldArr.length
        const arr = Array.from(new Set(oldArr.concat(newArr)))
        //如果新旧length 一样无更新
        if (arr.length === base) {
            this.dispatch['no-update'].forEach(fn => {
                fn()
            })
        
        } else {
            //否则通知更新
            this.dispatch['update'].forEach(fn => {
                fn()
            })
        }
    }

    timing(time = 10000) {
         //轮询
        setInterval(async () => {
            const newHtml = await this.getHtml()
            this.newScript = this.parserScript(newHtml)
            this.compare(this.oldScript, this.newScript)
        }, time)
    }

}
//实例化该类
const up = new Updater({
    timer:2000
})
//未更新通知
up.on('no-update',()=>{
   console.log('未更新')
})
//更新通知
up.on('update',()=>{
    console.log('更新了')
})
以上方案来源于:https://juejin.cn/post/7185451392994115645,作者:小满zs

4.补充方案:

不过通过以上方式来实现,功能是没问题的,但是轮询和加载html都是比较消耗资源的行为。分享其它2种纯前端的实现思路,供大家参考:

1、如果不使用轮询,可以通过全局捕获错误,判断是资源文件加载失败,就提示:用户刷新页面。

2、或者在import组件的时候,通过catch判断加载失败,提示更新也行。

链接: https://www.fly63.com/article/detial/12353


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK