vite生产环境报错,TypeError: Failed to fetch dynamically imported module: xxx

vite生产环境报错,TypeError: Failed to fetch dynamically imported module: xxx

1.问题描述

vue + vite项目:有时跳转页面会卡住,但刷新一下页面又恢复正常,控制台信息如下:

TypeError: Failed to fetch dynamically imported module: xxx

2.信息收集

用户端复现了问题,从现场获取到控制台日志:

img_16739282021590.png

3.问题分析

从报错来看,动态加载模块失败

单独访问module链接,服务端响应404

查看服务器文件,发现服务端文件,与请求文件的hash不一样, 也就是说服务端文件发生了变更,只有一种情况下,服务端文件会发生变更:有版本发布,并且文件代码有更新,才会导致文件hash值变化。

问题推测:用户在发布版本之前已经打开效能管理平台,在版本发布之后,用户跳转有代码更新的页面便会失败

问题证明:询问用户是在什么时候打开的页面,并通过对比鲸云版本发布时间点,对比结果符合上面的猜想,并通过自定义版本号,在测试环境成功复现问题。

到这里,问题基本确定。

4.问题解决

接下来是如何解决这个问题

问题的本质是:版本发布后,某些资源的链接可能发生变化,如果有用户正打开版本更新前的页面,那么此时跳转页面便有可能失败

问题解决思路:在版本发生变更时,自动重载页面,如此便会获得最新的资源链接

实现思路:

  1. 首先需要标记版本号:版本号已经存在于Jenkinsfile,不过该文件与js文件分离,需要从其中读取出来

  2. 本质上js文件都会下发到客户端,在客户端执行,但浏览器又存在js缓存,因此不能从js变量读取version,读取到的都是发布前的版本号

  3. 可以每次进行代码构建时创建一个version.txt,将版本号写入该文件,并且将该文件在服务端暴露,并服务端设置txt文件不缓存,前端可通过http请求,访问version.txt, 则可读取到最新的版本号

  4. 读取服务端版本号时机选择:有两种方式,一是定时轮询(确定是比较耗费资源),二是用户点击新链接时查询版本号(可在路由守卫中实现,缺点是会影响页面跳转速度)

  5. 由于上面问题是一个较小概率事件,不应该对解决该问题花费过多的代价

    基于方案二,能否在不影响跳转速度的同时,获取最新版本号?可以,也有两种方法:1.使用异步方式获取版本号,2.使用web worker获取版本号

    • 异步方式缺陷:出现异常可能会影响到主线程,异步代码会在同步代码执行完之后进行,版本检测速度会有影响

    • 使用web worker可以解决异步的缺陷,web worker工作在单独的线程,如果worker出现异常不会影响主线程,在多核CPUworker与主线程并行执行,速度会更快,

    • 现代浏览器对web worker的支持也比较不错:

image-20230111103751340.png

5.代码实现

基于以上思路,通过代码实现

读取Jenkinsfile版本号,定义全局js变量__APP_VERSION__(客户端存储的版本号), 创建静态资源version.txt(服务端当前版本号)

./vite.config.js

import fs from 'fs';

...

// 从Jenkinsfile读取版本号,并生成version.txt
const jenkinsContent = fs.readFileSync(resolve(__dirname, 'Jenkinsfile')).toString();
const versionMatch = jenkinsContent.match(/VERSION\s*=\s*'(\S*)'/);
const versionCode = versionMatch[1];
console.log(`Current application version: ${versionCode}`);
// public文件夹下的内容会原封不动地打包到dist
fs.writeFileSync(resolve(__dirname, 'public/version.txt'), versionCode);

export default ({ mode }) => {
    ... ...
    // 定义全局常量
    define: {
      __APP_VERSION__: JSON.stringify(versionCode),
    },
  });
};

发送请求访问version.txt资源,读取服务端版本号,如果有更新就通知主线程

./workers/versionCheckWorker.js

// 检测版本worker
import axios from 'axios';

const versionFileUrl = `${import.meta.env.VITE_APP_BASE_URL}version.txt`;

const instance = axios.create({
  timeout: 3000,
  headers: { 'Cache-Control': 'no-cache' },
});

self.onmessage = e => {
  const data = e.data;
  const currentVersion = data.currentVersion;
  const toPath = data.toPath;
  // 获取远端版本
  instance
    .get(versionFileUrl)
    .then(res => {
      if (res.status === 200) {
        self.postMessage({ hasChange: currentVersion != res.data, remoteVersion: res.data, toPath });
      } else {
        console.error(res);
      }
    })
    .catch(e => console.error(e));
};

self.onmessageerror = e => console.error(e);

在路由守卫中监听用户跳转,并将当前版本号发送给worker线程,如果检测到版本更新,就刷新页面

./router/index.js

import versionCheckWorker from '../workers/versionCheckWorker?worker&inline';

... ...

router.beforeEach(async to => {
  ... ...
  versionCheck(to);
  ... ...
});

/**
 * 通过web worker检测版本发布,以修复版本发布时,用户长时间停留页面导致无法跳转的问题
 */
const ENABLE = '1';
let checkWorker = null;
/* eslint-disable no-undef */
let appVersion = __APP_VERSION__;
if (import.meta.env.VITE_CHECK_VERSION_ENABLE === ENABLE && window.Worker) {
  checkWorker = new versionCheckWorker();
  checkWorker.onmessage = e => {
    const data = e.data;
    if (data && data.hasChange) {
      console.log(`远端版本版本发生变化,强制刷新页面,远端版本号:${data.remoteVersion}, 本地版本号: ${appVersion}`);
      appVersion = data.remoteVersion;
      // 刷新页面
      window.location.assign(`${import.meta.env.VITE_APP_BASE_URL}${data.toPath}`);
    }
  };
  console.log(`已启用版本检测,当前版本:${appVersion}`);
}

/**
 * 版本检查
 */
const versionCheck = to => {
  if (checkWorker) {
    checkWorker.postMessage({ currentVersion: appVersion, toPath: to.fullPath });
  }
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容