• 如何解决前端上线之后用户页面不刷新的问题


    前言

    最近使用 Vue3 技术把公司的一个大的 Web 前端项目进行了一个升级。升级过程坎坷不断,但最终是成功了,具体升级相关的另开篇讲,这里主要讲下使用 Vite 作为打包工具之后,每次打包上线完,用户客户端页面路由跳转报错,必须要主动刷新页面才可获取最新资源的问题。

    起因

    刚刚说了,导致这个问题的具体原因,是因为在 vue-router 里使用了页面模块懒加载,通过 Vite 打包之后,不同于 Webpack ,生成的 dist/assite 文件夹中,会有很多个小的 js 文件,而 Webpack 打包会生成一个大的 app.js 文件。这样的好处是,大大提升了项目首页开屏的速度!但是缺点上完线之后就暴露出来了,由于上线打包,使用自动化工具,会把之前的项目文件清空,然后把 build 之后的文件移动过来,相当于整个做了一个替换!所以如果客户端不主动刷新浏览器,用户的本地项目还是上一个版本的,当它需要进行跳转页面或者引入其他的包时,自然在服务器上找不到之前的包了 -_-!

    方案

    针对这个问题,我想了以下几种解决的办法:

    1. 通过前后端配合定义一个版本接口

    这种场景就好像是 App 版本更新似的,每次上线都要定义一个版本,这个版本通过后端接口进行修改和获取,但是需要前端和后端一起配合,以及一整套流程控制,数据表维护等。实现起来相对来说较麻烦,需要一定时间。

    2. 前端打包生成一个json文件记录版本,路由跳转的时候,通过请求服务端版本号与本地版本号是否一致,决定是否刷新

    这种相当于第一种方案的简化版(不需要后端配合)。只需要在打包的时候,自己生成一个 json 文件来记录本次打包的版本号就可以了,这个版本号不一定要按真实的版本来,只需要是唯一的就行了,换句话说,可以生成一个随机码,uaaId 、随机数、时间戳都可以。这种可以完全自动化,弄好之后,不需要人工去改什么了。

    3. 记录路由跳转时间,每隔6个小时刷新路由

    简化版,简单粗暴,直接通过记录页面跳转的时间,来判断是否刷新,因为上线一般是晚上进行,所以第二天,基本也都能执行刷新动作了,但是不够及时。万一不够时间,还是会有这个问题。

    4. 退出登录的时候刷新页面

    作为一个补充。

    5. 重写push方法,捕捉错误

    这个 Vue3 中我没有实现过,只是作为一个思路,如果能在路由 push 报错的时候,捕捉错误并且刷新页面,貌似也是可行。

    解决

    最终,我选择了第二种方法,解决了这个问题,相对来说不需要后端配合也减少了工程量。以下是解决的具体实现:

    1. 项目根目录下新建 build/build.js

    在这里插入图片描述

    build.js 代码:

    console.log('build > 文件开始执行!')
    const fs = require('fs')
    const path = require('path')
    
    function getRootPath(...dir) {
      return path.resolve(process.cwd(), ...dir)
    }
    const runBuild = async () => {
      try {
        const OUTPUT_DIR = 'dist'
        const VERSION = 'version.json'
        const versionJson = {
          version: 'V_' + Math.floor(Math.random() * 10000) + Date.now()
        }
        fs.writeFileSync(getRootPath(`${OUTPUT_DIR}/${VERSION}`), JSON.stringify(versionJson))
        console.log(`version file is build successfully!`)
      } catch (error) {
        console.error('version build error:\n' + error)
        process.exit(1)
      }
    }
    runBuild()
    console.log('build > 文件执行结束!')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    通过 node 在打包完成以后生成一个 version.json 文件。同时生成一个随机码作为区分版本的 ID。

    2. 在 package.json 中增加指令

    在这里插入图片描述

    package.json

    "postbuild": "node ./build/build.js",
    
    • 1

    此指令会在 build 之后自动被调用执行。具体不明白可参看这篇文章:
    package.json中的scripts命令解析

    3. 在 router/index.js 中处理版本更新

    router/index.js

    import axios from 'axios'
    
    // 路由拦截
    router.beforeEach((to, from, next) => {
      // ... 其他逻辑
      
      // 检查版本更新
      if (from.path !== '/') {
        checkAppNewVersion()
      }
     
    })
    
    // 检查服务端是否已经更新,如果更新刷新页面
    async function checkAppNewVersion() {
      const url = `/version.json?t=${Date.now()}`
      let res = null
      try {
        res = await axios.get(url)
      } catch (err) {
        console.error('checkAppNewVersion error: ', err)
      }
      if (!res) return
      const version = res.data.version
      const localVersion = Local.get(localKeys.APP_VERSION)
      if (localVersion && localVersion !== version) {
        Local.set(localKeys.APP_VERSION, version)
        window.location.reload()
      }
      Local.set(localKeys.APP_VERSION, version)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    这里之所以是在 beforeEach 钩子里处理,是有原因的,afterEach 和 beforeResolve 是在 beforeEach 执行之后执行,当服务器文件已经更新了,然后本地还没更新的情况下,是加载不到即将要跳转的页面文件的。
    在这里插入图片描述
    beforeResolve 执行的时候,说明所需要加载的组件已经加载好了,如果加载不到文件的话,这里已经开始抛错了。

    以上基本已经处理完了,服务端上线了新的文件之后,本地页面跳转的时候根据拿到的 version 和本地的进行对比,如果不同就先刷新页面,重新获取最新资源文件。

    但是这样做还是有个小问题:
    当用户在登录页时,服务端更新了版本,所以用户在输入用户名和密码之后,点击登录,显示登录成功,正常是应该直接跳到首页,但是此时跳转前检测到版本更新,所以会原地刷新一下获取最新资源,导致页面又恢复到了登录之前的状态,用户需要重新登录。这点体验很不好。所以后期加了以下处理:

    // 监听页面打开显示
    document.addEventListener('visibilitychange', function () {
      // console.log('show ===>', document.visibilityState, !document.hidden)
      if (!document.hidden) {
        checkAppNewVersion()
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    增加了页面显示的监听,在用户打开页面时,先获取版本更新一下,这样就可以避免这个问题。

    感谢阅读,如果这篇文章对你有帮助的话,帮忙点个赞吧,谢谢!

  • 相关阅读:
    软件测试SQL面试题(初)
    win11家庭版docker和milvus
    【计算机毕业设计】新冠疫情隔离人员信息管理系统+vue源码
    重装了电脑系统,推荐一些 Windows 系统装机必备软件
    内存管理(五)——内存回收
    推特大裁员后,马斯克与白宫发生冲突!META 大批裁员正在路上
    Mybatis中${}和#{}的区别
    简易版Pycharm(2023)+Conda开发环境配置教程
    if _name_ == “__main__“:NameError: name ‘_name_‘ is not defined
    Spring/Spring MVC、Spring Boot/Spring Cloud
  • 原文地址:https://blog.csdn.net/Kevinblant/article/details/126237531