首页 文章

如何强制更新单页应用程序(SPA)页面?

提问于
浏览
31

在基于服务器端的完全呈现(非Web 2.0)中,部署服务器端代码将在页面重新加载时直接更新客户端页面 . 相比之下,在基于React的单页应用程序中,即使在更新了React组件之后,仍然会有一些客户端使用旧版本的组件(它们仅在浏览器重新加载时获得新版本,这应该很少发生) - >如果页面完全是SPA,有些客户可能会在几个小时后刷新页面 .

应采用哪些技术来确保任何客户不再使用旧组件版本?

更新:API未更改,只有更新版本更新了React组件 .

5 回答

  • 34

    在应用程序加载时,您可以让React组件向服务器发出ajax请求,以获取"interface version" . 在服务器API中,您可以维护客户端版本的增量值 . React组件可以将此值存储在客户端(cookie /本地存储/等)上 . 当它检测到更改时,它可以调用 window.location.reload(true); ,它应该force the browser to discard client cache并重新加载SPA . 或者更好的是,通知最终用户将加载新版本并询问他们是否希望保存工作然后重新加载等取决于您想要做什么 .

  • 4

    与史蒂夫泰勒的答案类似,但不是版本化API endpoints ,而是按照以下方式对客户端应用程序进行版本控制 .

    每个HTTP请求都会发送一个自定义标头,例如:

    X-Client-Version: 1.0.0

    然后,服务器将能够拦截这样的头部并相应地做出响应 .

    如果服务器知道客户端的版本是陈旧的,例如,如果当前版本是 1.1.0 ,则使用将由客户端正确处理的HTTP状态代码进行响应,例如:

    418 - I'm a Teapot

    然后可以通过以下方式对客户端进行编程以通过刷新应用程序来响应此类响应:

    window.location.reload(true)

    基本前提是服务器知道最新的客户端版本 .

    编辑:

    给出了类似的答案here .

  • 2

    您可以使用来自API任何 endpoints 的每个响应发送应用版本 . 因此,当应用程序发出任何API请求时,您可以轻松检查是否有新版本,并且您需要进行硬重新加载 . 如果API响应中的版本比localStorage中存储的版本新,请设置 window.updateRequired = true . 你可以使用以下反应组件来包装 react-routerLink

    import React from 'react';
    import { Link, browserHistory } from 'react-router';
    
    const CustomLink = ({ to, onClick, ...otherProps }) => (
      <Link
        to={to}
        onClick={e => {
          e.preventDefault();
          if (window.updateRequired) return (window.location = to);
          return browserHistory.push(to);
        }}
        {...otherProps}
      />
    );
    
    export default CustomLink;
    

    并在整个应用程序中使用它而不是react-router的 Link . 因此,只要有更新并且用户导航到另一个页面,就会有重新加载,用户将获得最新版本的应用程序 .

    您还可以显示一个弹出窗口:“有更新,请单击[此处]启用它 . ”如果您只有一个页面,或者您的用户很少导航 . 或者只是重新加载应用程序而不要求 . 这取决于您的应用和用户 .

  • 3

    应采用哪些技术来确保任何客户不再使用旧组件版本?

    今天(2018年),许多前端应用程序使用service workers . 有了它,可以通过多种方式管理您的应用程序生命周期 .

    这是第一个示例,通过使用ui通知,要求访问者刷新网页以获取最新的应用程序版本 .

    import * as SnackBar from 'node-snackbar';
    
    // ....
    
    // Service Worker
    // https://github.com/GoogleChrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
    
    const offlineMsg = 'Vous êtes passé(e) en mode déconnecté.';
    const onlineMsg = 'Vous êtes de nouveau connecté(e).';
    const redundantMsg = 'SW : The installing service worker became redundant.';
    const errorMsg = 'SW : Error during service worker registration : ';
    const refreshMsg = 'Du nouveau contenu est disponible sur le site, vous pouvez y accéder en rafraichissant cette page.';
    const availableMsg = 'SW : Content is now available offline.';
    const close = 'Fermer';
    const refresh = 'Rafraîchir';
    
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        function updateOnlineStatus() {
          SnackBar.show({
            text: navigator.onLine ? onlineMsg : offlineMsg,
            backgroundColor: '#000000',
            actionText: close,
          });
        }
        window.addEventListener('online', updateOnlineStatus);
        window.addEventListener('offline', updateOnlineStatus);
        navigator.serviceWorker.register('sw.js').then((reg) => {
          reg.onupdatefound = () => {
            const installingWorker = reg.installing;
            installingWorker.onstatechange = () => {
              switch (installingWorker.state) {
                case 'installed':
                  if (navigator.serviceWorker.controller) {
                    SnackBar.show({
                      text: refreshMsg,
                      backgroundColor: '#000000',
                      actionText: refresh,
                      onActionClick: () => { location.reload(); },
                    });
                  } else {
                    console.info(availableMsg);
                  }
                  break;
                case 'redundant':
                  console.info(redundantMsg);
                  break;
                default:
                  break;
              }
            };
          };
        }).catch((e) => {
          console.error(errorMsg, e);
        });
      });
    }
    
    // ....
    

    还有一种优雅的方法来检查后台升级,然后在用户单击内部链接时静默升级应用程序 . 此方法在ach.codes上提出,并在此thread上进行了讨论

  • 7

    在服务器端渲染中是,如果您需要更新页面的一小部分,则还需要重新加载整个页面 . 但是在SPA中你使用ajax更新你的东西,因此无需重新加载页面 . 看到你的问题我有一些假设:

    您会看到其中一个组件已更新,但从同一API获取数据的其他组件未更新 . 这是 Flux Architecture . 如果你的数据存储在你的组件中,你的组件会监听存储's changes, whenever data in your store changes all your components listening to it'的更改将被更新(没有缓存的场景) .

    要么

    您需要控制组件自动更新 . 为了那个原因

    • 您可以按特定时间间隔向服务器请求数据

    • Websockets 可以帮助您从服务器更新组件数据 .

相关问题