最近测试和用户频繁反馈,说经常会出现页面点不动了。然后每次都回复,刚发完版,刷新下就好了。但时间长了,就被怀疑是能力问题,因为其他项目为什么不这样呢?

先做一些准备工作,复盘一下当时的场景:

运行 pnpm create vite vue3-vite-router --template vue 在本地快速新建一个项目

进入项目目录中,运行pnpm i 安装依赖,安装成功后启动项目pnpm dev, 启动成功后运行 pnpm build进行打包。打包完成后根目录下会多出一个dist目录。运行 pnpm preview预览构建后的项目。

页面可以正常打开:

页面窗口保持不变(不进行刷新等其他操作)。在AboutView.vue文件中随便修改一点修改。再次运行打包pnpm build和预览 pnpm preview。在刚才的页面中点击 About 进行导航切换后,About页面对应的js文件请求失败了。

经过排查,是发版后js的文件名hash变了,原页面对应的html文件引用的js hash名称还是上一版本的,由于做了懒加载,所以点击跳转新页面时,对应的js文件不存在导致。懒加载的优化变成了事故,我冤枉啊~~~(我发誓:以后能跑起来绝对不优化)

为了证明自己的开发能力(掉发能力),针对vite项目调研了两种方案(纯前端方案,绝不麻烦后端):

翻看vite官方文档上发现了一个简单的办法:当vite加载动态导入失败时,会触发 vite:preloadError 事件。可以使用这个事件进行处理。

在main.js中增加以下代码:

 1window.addEventListener('vite:preloadError', (event) => {
 2  console.log('检测到有新版本,5秒后即将刷新...');
 3  setTimeout(() => {
 4    window.location.reload() 
 5    console.log('页面已更新为最新版本...');
 6  }, 5000)
 7})

先执行 pnpm buildpnpm preview,刷新页面。然后随意修改AboutView.vue的代码,重新执行 pnpm buildpnpm preview,点击About后,js请求异常,会触发vite:preloadError事件,然后执行回调中的刷新逻辑。

总结:这个方法比较简单,但是只能应用在路由切换的场景中。

还有一种场景是无路由切换时,代码未及时进行更新。对我们的项目来说,都是在晚上发布,如果有版本更新,运维同学会在群里及时同步测试和产品同学。如果需要解决,需要进行轮询操作。具体请参考:2种纯前端检测版本更新提示

目测最佳的时间方案为:

vite打包构建时生成版本号json文件,并将json文件保存到构建好的静态资源目录中。

路由变化时执行:对比json文件中的版本号与缓存中是否一致。若一致,则进行弹窗提示用户进行刷新。

相关代码参考了这篇文章:vite前端版本升级,刷新页面

  1. 新建一个vite插件,完成将发布版本的时间戳写入文件。代码如下:
 1import path from "path";
 2import fs from "fs";
 3
 4
 5const writeVersion = async (versionFile, content) => {
 6  fs.writeFile(versionFile, content, (err) => {
 7    if (err) throw err;
 8  });
 9};
10
11export default (options) => {
12  
13  let configPath;
14  return {
15    name: "refreshVersion",
16    configResolved(resolvedConfig) {
17      
18      configPath = resolvedConfig.publicDir;
19    },
20    async buildStart() {
21      
22      const file = configPath + path.sep + "version.json";
23      
24      const content = JSON.stringify({ version: options.version });
25      if (fs.existsSync(configPath)) {
26        
27        writeVersion(file, content);
28      } else {
29        
30        fs.mkdir(configPath, (err) => {
31          if (err) throw err;
32          writeVersion(file, content);
33        });
34      }
35    },
36  };
37};
  1. vite配置文件中使用该插件,并在全局缓存版本时间戳,便于后续比对使用。代码如下:
 1import { fileURLToPath, URL } from 'node:url'
 2
 3import { defineConfig } from 'vite'
 4import vue from '@vitejs/plugin-vue'
 5import refreshPlugin from './src/plugins/refreshPlugin.js'
 6const now = new Date().getTime(); 
 7
 8
 9
10export default defineConfig({
11  plugins: [
12    vue(),
13    
14     refreshPlugin({
15      version: now
16    })
17  ],
18  resolve: {
19    alias: {
20      '@': fileURLToPath(new URL('./src', import.meta.url))
21    }
22  },
23  
24  define: {
25    __APP_VERSION__: now,
26  }
27})
 1router/index.js
 2
 3
 4
 5
 6router.beforeEach(async (to, from, next) => {
 7  await versionCheck() 
 8  next()
 9});
10
11
12const versionCheck = async () => {
13  if (process.env.NODE_ENV === 'development') return
14  const response = await axios.get('version.json')
15  if (__APP_VERSION__ !== response.data.version) {
16      
17      console.log('有新版本,5秒后自动刷新页面');
18      setTimeout(() => {
19          window.location.reload()
20      }, 5000)
21  }
22}

与第一种方案相同操作,先后运行两次 pnpm buildpnpm preview。结果显示,第二次build后,路由切换自动刷新完成。

总结:本文提出了vite项目中版本变化的刷新问题,通过监听vite异常事件事件维护比对发布版本时间戳两种方案,成功解决了自动刷新问题,并展示了demo相关代码和运行结果。

个人笔记记录 2021 ~ 2025