You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级react-scripts和webpack后,React构建无法让浏览器获取最新版本求助

问题描述

升级react-scripts从v1.1.5到v2.0.5、webpack从v3.12.0到v4.19.1后,仅npm run build环节出现异常:

  1. 初始构建时,无论修改代码或重复执行构建,chunk文件的chunkhash始终固定不变,无法生成唯一哈希文件名;
  2. 将webpack配置中的[chunkhash:8]替换为[hash]后,哈希文件名能正常更新,但浏览器检测到新版本并自动刷新后,仍显示旧内容,必须通过Ctrl+F5硬刷新才能加载新代码。
    怀疑问题出在service worker,附注册代码如下:
// In production, we register a service worker to serve assets from local cache.

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.

// This link also includes instructions on opting out of this behavior.

const isLocalhost = Boolean(
  window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export default function register() {
  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
      return;
    }

    window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

      if (!isLocalhost) {
        // Is not local host. Just register service worker
        registerValidSW(swUrl);
      } else {
        // This is running on localhost. Lets check if a service worker still exists or not.
        checkValidServiceWorker(swUrl);
      }
    });
  }
}

function registerValidSW(swUrl) {
  navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        installingWorker.onstatechange = () => {
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and
              // the fresh content will have been added to the cache.
              // It's the perfect time to display a "New content is
              // available; please refresh." message in your web app.

              //hard refresh
              window.location.reload(true);
              console.log('New content is available; please refresh.');
            } else {
              // At this point, everything has been precached.
              // It's the perfect time to display a
              // "Content is cached for offline use." message.
              console.log('Content is cached for offline use.');
            }
          }
        };
      };
    })
    .catch(error => {
      console.error('Error during service worker registration:', error);
    });
}

function checkValidServiceWorker(swUrl) {
  // Check if the service worker can be found. If it can't reload the page.
  fetch(swUrl)
    .then(response => {
      // Ensure service worker exists, and that we really are getting a JS file.
      if (
        response.status === 404 ||
        response.headers.get('content-type').indexOf('javascript') === -1
      ) {
        // No service worker found. Probably a different app. Reload the page.
        navigator.serviceWorker.ready.then(registration => {
          registration.unregister().then(() => {
            window.location.reload();
          });
        });
      } else {
        // Service worker found. Proceed as normal.
        registerValidSW(swUrl);
      }
    })
    .catch(() => {
      console.log(
        'No internet connection found. App is running in offline mode.'
      );
    });
}

export function unregister() {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.ready.then(registration => {
      registration.unregister();
    });
  }
}
解决方案

一、修复chunkhash不更新的问题

Webpack 4对chunkhash生成逻辑做了调整,且react-scripts v2默认已配置正确的内容哈希策略,问题源于自定义配置覆盖了默认逻辑:

  • 替换为contenthash而非hash:[hash]是全局构建哈希,任何文件变更会导致所有文件名变化,不符合按需缓存需求;Webpack 4+推荐使用[contenthash],仅基于文件内容生成哈希,比chunkhash更精准。修改配置为:
    filename: 'static/js/[name].[contenthash:8].js',
    chunkFilename: 'static/js/[name].[contenthash:8].chunk.js'
    
  • 确保分离runtime chunk:react-scripts v2默认会将webpack runtime代码分离为单独chunk,避免因runtime变化导致所有chunk哈希变更。若自定义配置移除了该逻辑,需添加:
    optimization: {
      runtimeChunk: 'single',
    }
    

二、修复service worker缓存不更新问题

你的service worker注册代码存在两处关键问题,导致新版本无法及时生效:

1. 新service worker未强制激活

默认情况下,新service worker会进入等待状态,直到所有打开页面关闭才激活。需在服务器上的service-worker.js文件(非注册代码)中添加激活逻辑:

// 在service-worker.js顶部添加
self.addEventListener('install', event => {
  // 跳过等待,直接激活新service worker
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', event => {
  // 接管所有打开的客户端页面
  event.waitUntil(self.clients.claim());
  // 清除旧缓存(可选,根据缓存策略调整)
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(name => name !== 'your-cache-name').map(name => caches.delete(name))
      );
    })
  );
});

2. 注册代码的刷新时机错误

当前在installingWorker.state === 'installed'时立即刷新,但此时新service worker尚未激活,旧worker仍控制页面,刷新会读取旧缓存。修改注册代码中的onstatechange逻辑:

installingWorker.onstatechange = () => {
  if (installingWorker.state === 'installed') {
    if (navigator.serviceWorker.controller) {
      console.log('新版本已就绪,请刷新页面');
      // 监听新worker激活事件后再刷新
      registration.addEventListener('activate', () => {
        window.location.reload();
      });
    } else {
      console.log('内容已缓存,可离线使用');
    }
  }
};

3. 确保service-worker.js不被缓存

服务器需配置service-worker.js的缓存策略为不缓存,否则浏览器会一直使用旧文件,无法检测新版本。例如nginx配置:

location /service-worker.js {
  add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
  expires off;
  proxy_no_cache 1;
}

三、额外检查点

  • 确认process.env.PUBLIC_URL配置正确,service worker能正常加载;
  • 部署新版本后,检查服务器上的service-worker.js和静态资源文件名是否已更新;
  • 在浏览器开发者工具Application > Service Workers面板中,查看service worker状态,确认新版本已激活。

内容的提问来源于stack exchange,提问作者June Leow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:37:03