点击开始动手实验


问题背景

把0.1秒就能复现的“白屏”丢给测试同学,他们会在群里疯狂艾特你;丢给老板,他只会问“多久能好”。Chat{GPT} 登录后页面空白就是这样一种“零报错、零提示、零线索”的三零故障——开发者本地一切正常,一到线上就只剩一块惨白的画布。

  • 调试窗口没有 4xx/5xx,Network 面板甚至全是 200
  • 路由明明跳转到 /chat,却连侧边栏都没渲染出来
  • 刷新一次好了,再刷新又白屏,复现规律完全看运气

在迭代节奏按小时计算的今天,白屏每多存在 10 分钟,需求池就会多 3 个紧急插单。效率被拖垮的往往不是代码复杂度,而是“看不见的错误”带来的定位成本。

常见原因分析

把能触发白屏的环节画成一条“登录→认证→拉配置→渲染”流水线,80% 的故障都能归类到下面 5 个点:

  1. 认证令牌失效或作用域不足
    典型场景:后端刷新 access_token 时把 session_key 写丢,前端拿到 401 却未触发重登。

  2. 前端路由守卫死循环
    异步鉴权结果返回前,路由守卫提前 next() 到空白页;或者 next(false) 写成 next(),直接中断渲染。

  3. API 响应格式变更
    例如 /v1/me 接口把 user.name 改成 user.username,模板里直接 {{ user.name }} 导致整页 Vue 报错被 errorCaptured 吞掉,呈现就是白屏。

  4. 骨架屏/微前端挂载失败
    子应用打包后 publicPath 带版本号,Nginx 没配 try_filesvendor.js 404,主应用继续等待 bootstrap 生命周期,页面永远空白。

  5. Content-Security-Policy 拦截
    开了严格 CSP 却忘记把 'unsafe-inline'script-src,导致内联脚本被拦,框架初始化直接失败。

技术解决方案

下面给出一条“先保活、再定位、最后根治”的三板斧流程,按顺序执行,基本能在 15 分钟内确认是前端还是后端的责任。

  1. 复现并保留现场
    打开无痕窗口,用“Preserve log”+“Disable cache”抓一次完整请求链,导出 HAR 留档,防止后续刷新把错误冲掉。

  2. 快速自检令牌
    在 Console 里执行
    await (await fetch('/v1/me',{headers:{Authorization:Bearer ${localStorage.token}}})).json()
    如果返回 401,直接跳到第 4 步;若能拿到用户信息,说明令牌有效,排除认证问题。

  3. 确认框架是否启动
    Vue 项目看 __VUE_PROD_DEVTOOLS__ 是否存在;React 项目看 root._internalRoot。未定义即说明框架源码都没跑起来,优先检查脚本 404 或 CSP。

  4. 手动刷新令牌
    调用 /v1/refreshrefresh_token 换成新 access_token,写回 localStorage 后强制 location.reload()。白屏消失则证明是静默刷新逻辑没跑通。

  5. 打开框架级错误捕获
    在入口文件最顶部加:

    window.addEventListener('error', e => console.trace('Global Error:', e));
    window.addEventListener('unhandledrejection', e => console.trace('Unhandled Promise:', e));
    

    重新构建上线,白屏时马上看日志,通常能抓到模板渲染异常或路由死循环的栈。

  6. 本地代理比对
    用 Whistle/Charles 把线上域名映射到本地打包目录,若本地正常而线上白屏,99% 是资源路径或 CDN 跨域问题。

代码示例

以下示例基于 Vue3 + Vue-Router 4,其他框架思路一致,可直接迁移。

  1. 路由守卫:保证异步鉴权完成后再放行

    // router/index.js
    import { getUserInfo } from '@/api/user';
    
    router.beforeEach(async (to, from, next) => {
      if (to.meta?.requiresAuth) {
        try {
          // 优先读缓存,没有再请求
          if (!store.user) await getUserInfo();
          next();                // 鉴权成功
        } catch (e) {
          next('/login');        // 失败就回登录页,避免空白
        }
      } else {
        next();                  // 公开页面直接放行
      }
    });
    
  2. 令牌自动刷新:在 axios 响应拦截里做静默刷新

    // utils/request.js
    let isRefreshing = false;    // 防止并发刷新
    axios.interceptors.response.use(
      res => res,
      async err => {
        const { config, response } = err;
        if (response?.status === 401 && !config._retry) {
          config._retry = true;
          if (!isRefreshing) {
            isRefreshing = true;
            await refreshToken();          // 调用刷新接口
            isRefreshing = false;
          }
          return axios(config);            // 重试原请求
        }
        return Promise.reject(err);
      }
    );
    
  3. 全局错误兜底:把白屏转成可读的报错页

    // main.js
    import { createApp } from 'vue';
    import ErrorComp from '@/components/Error.vue';
    
    const app = createApp(App);
    app.config.errorHandler = (err, vm, info) => {
      console.error('[Render Error]', err, info);
      vm.$root.$el.replaceWith(ErrorComp.$el);   // 把整页替换成错误提示
    };
    

性能与安全考量

  • 令牌存储:禁止放 Cookie 的 HttpOnly=false 里,避免 XSS 直接盗取;推荐内存 + 刷新令牌双轨制,内存变量页面关闭即失效。
  • 刷新风暴:并发 401 时,必须加 isRefreshing 锁,否则 N 个请求会同时换 N 次令牌,后端极易被误判为“刷接口”。
  • 脚本加签:若用 CDN,开启 SRI(Subresource Integrity)并在 CSP 加 require-sri-for script,防止劫持注入。
  • 白屏重试次数:对静默刷新失败设置上限(建议 2 次),超过后强制跳转登录页,避免无限循环请求把流量打满。

避坑指南

  1. 本地代理正常 ≠ 线上正常,一定在“禁用缓存 + 慢 3G”模式再测一次,很多 404 是 CDN 边缘节点延迟同步导致的。
  2. 骨架屏图片请用 base64 内嵌,不要走外链;一旦 CSP 拦了 img-src 里的 self,首屏就白给你看。
  3. 微前端场景下,主应用和子应用使用同一套 key 管理令牌,刷新接口必须幂等,避免子应用刷新后主应用令牌失效。
  4. 日志上报要“先落地、后上报”,如果白屏导致框架未初始化,依赖框架的上报 SDK 也发不出去,用 `
<script></script>

兜底。 5. 灰度发布时,把新旧版本放在不同子目录,用try_files $uri $uri/ /dist-xxx/index.html` 兜底,防止路径 404 引发白屏。

互动环节

你在集成 ChatGPT 或其他大模型网页时,还遇到过哪些“无声”的白屏场景?
欢迎在评论区贴出报错截图或请求链,我会挑典型问题更新到正文,并附上后续排查录屏。一起把白屏耗时从小时级压到分钟级。


把排查流程跑通后,我发现很多“灵异白屏”其实都能复用到其他实时交互场景——比如最近我在从0打造个人豆包实时通话AI动手实验里,同样用到了“令牌保活 + 全局错误兜底”思路,让语音链路在弱网环境下也能秒级重连,全程没再出现“说了话没反应”的空白尴尬。实验把 ASR→LLM→TTS 整条链路拆成可插拔模块,跟着跑一遍,你会对“实时交互白屏”有更深的体感。

点击开始动手实验


Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐