问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

浏览器缓存机制导致的白屏问题解决

创作时间:
作者:
@小白创作中心

浏览器缓存机制导致的白屏问题解决

引用
CSDN
1.
https://blog.csdn.net/Andrew_Chenwq/article/details/145636752

项目部署更新后,用户反应长时间白屏,多次刷新后仍然白屏。本文将详细介绍如何排查和解决这一问题,重点讨论了通过Nginx配置和代码层面的解决方案。

问题描述

项目部署更新后,用户反应长时间白屏,多次刷新后仍然白屏。

问题排查

用户那边多次刷新后仍然白屏无法显示,首先可以排除网络问题导致的加载缓慢,其次部署后我访问是正常的,也可以排除代码报错导致的白屏原因(react项目若没有做错误边界处理,JS发生错误也会使其白屏), 那么问题其实很明显了就是资源文件的错误加载导致无法正常渲染。

解决方案

注意: 一般普通用户的浏览器缓存是默认开启的,开发中我们防止缓存带来的更新延时,一般会自行关闭,但用户不是开发,是无法做到让每个用户去关闭缓存的,也不建议关闭缓存,因为缓存可以加快页面加载速度,提升用户体验,所以我们需要找到一种方法,既能保证用户访问时加载最新的资源文件,又能保证用户访问时加载的index.html是最新的,而不是缓存中的index.html。

  1. 利用nginx禁止index.html缓存,因为index.html是入口文件,若缓存了,那么即使资源文件更新了,用户访问的也是旧的资源文件,依旧导致白屏。
// nginx配置
server {
  location / {
    # 对 HTML 文件设置不缓存
    if ($request_filename ~* .*\.(html|htm)$) {
      add_header Cache-Control "no-cache, no-store, must-revalidate";
      add_header Pragma "no-cache";
      add_header Expires 0;
    }

    # 带哈希的静态资源长期缓存
    if ($request_filename ~* .*\.(css|js|png|jpg|jpeg|gif|ico|webp|svg)$) {
      expires 365d;
      add_header Cache-Control "public, immutable";
    }

    try_files $uri $uri/ /index.html;
  }
}
 // vite.config.ts
  build: {
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/js/[name]-[hash].js',
          entryFileNames: 'assets/js/[name]-[hash].js',
          assetFileNames: 'assets/[name]-[hash].[ext]',
          manualChunks(id) {
            if (id.includes('node_modules')) {
              return id.toString().split('node_modules/')[1].split('/')[0].toString();
            }
          },
        },
      },
    },

这个方法是最高效的,即保留了了浏览器缓存,又避免了index.html缓存带来的问题。

  1. 奈何我司运维不肯配合,只能采用第二种方案,在代码中监听资源文件的加载情况,若加载失败则强制刷新并清空缓存。

注意: 这里的刷新不是简单的刷新页面,而是强制刷新,即强制清除缓存,重新加载资源文件。

强制刷新清空缓存有以下几种方式

一. 利用window.location.reload(true)方法,当设置为’true’时,浏览器会绕过缓存,从服务器重新加载页面,不过这个参数存在浏览器兼容问题,可以排除。

二. 利用meta的特性, 但是某些浏览器可能不完全遵循 HTML 中的 http-equiv 缓存指令,服务器响应头始终优先级更高。

 <!-- 在 HTML 头部添加 -->
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Expires" content="0">

三. 最后,最简单请求的URL后面加上时间戳,这样每次请求的URL都是不同的,浏览器就不会缓存了。

 window.location.href = window.location.href.split('?')[0] + '?ts=' + Date.now();

完整代码

 // App.tsx

...
import { useEffect } from 'react';

function App() {
 // 监听白屏
 
 // 1. 利用addEventListener监听error事件,当资源文件加载失败时,强制刷新页面
 useEffect(() => {
   window.addEventListener(
     'error',
     (event) => {
       const target = event.target as Element | null;
       if (target && (target.tagName === 'SCRIPT' || target.tagName === 'LINK')) {
         window.location.href = window.location.href.split('?')[0] + '?ts=' + Date.now();
       }
     },
     true,
   );

   // 2.也可以利用性能监控这个API,监听资源加载情况,当资源加载失败时,强制刷新页面
   // const resourceObserver = new PerformanceObserver((list) => {
   //   list.getEntries().forEach((entry) => {
   //     console.log(entry, 'eeeee');
   //     if (entry.entryType === 'resource' && entry.duration === 0 && entry.transferSize === 0) {
   //       window.location.href = window.location.href.split('?')[0] + '?ts=' + Date.now();
   //     }
   //   });
   // });
   // // 启动监听
   // resourceObserver.observe({ entryTypes: ['resource'] });

   return () => window.removeEventListener('error', () => {});
 }, []);

 return (
   ...
 );
}
export default App;

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号