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

deikduxw  于 2023-04-20  发布在  React
关注(0)|答案(7)|浏览(378)

完全基于服务器端的渲染(非Web 2.0),部署服务器端代码会在页面重新加载时直接更新客户端页面。相比之下,在基于React的单页应用程序中,即使在React组件更新后,仍然会有一些客户端使用旧版本的组件(他们只在浏览器重新加载时获得新版本,这应该很少发生)-〉如果页面完全SPA,则可能一些客户端仅在几个小时后刷新页面。
应该采用什么技术来确保任何客户端都不再使用旧的组件版本?
更新:API没有改变,只有React组件更新了新版本。

lx0bsm1f

lx0bsm1f1#

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

eqqqjvef

eqqqjvef2#

与Steve Taylor的答案类似,但我不对API端点进行版本控制,而是按以下方式对客户端应用程序进行版本控制。
在每个HTTP请求中发送一个自定义头,例如:
X-Client-Version: 1.0.0
然后服务器将能够拦截这样的报头并相应地做出响应。
如果服务器知道客户端的版本已经过时,例如,如果当前版本是1.1.0,则使用将由客户端适当处理的HTTP状态代码进行响应,例如:
418 - I'm a Teapot
然后,客户端可以被编程为通过以下内容刷新应用程序来对这样的响应做出React:
window.location.reload(true)
基本前提是服务器知道最新的客户端版本。
编辑:
类似的答案是here

2w3rbyxf

2w3rbyxf3#

应该采用什么技术来确保任何客户端都不再使用旧的组件版本?
今天(2018年),许多前端应用使用service workers。有了它,可以通过多种方式管理应用生命周期。
这里是第一个例子,通过使用用户界面通知,要求您的访问者刷新网页,以获得最新的应用程序版本。

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);
    });
  });
}

// ....

还有一种优雅的方法可以在后台检查升级,然后在用户单击内部链接时静默升级应用程序。这种方法在zach.codes上展示,并在此线程中进行了讨论。

yfjy0ee7

yfjy0ee74#

您可以在API的任何端点的每个响应中发送应用的版本。这样当应用发出任何API请求时,您可以轻松检查是否有新版本,并且您需要硬重新加载。如果API响应中的版本比存储在localStorage中的版本更新,请设置window.updateRequired = true。您可以使用以下react组件 Package 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。因此,每当有更新时,用户导航到另一个页面,就会有一个硬重载,用户将获得最新版本的应用。
你也可以显示一个弹出窗口说:如果您只有一个页面或您的用户很少浏览,则会显示“有更新,请单击[此处]以启用它”。或者直接重新加载应用程序而不询问。这取决于您的应用程序和用户。

stszievb

stszievb5#

我知道这是一个老主题,服务工作者可能是最好的答案。但我有一个简单的方法,似乎是有效的:
我在我的“index.html”文件中添加了一个 meta标签:

<meta name="version" content="0.0.3"/>

然后,我在同一个文件夹中有一个非常简单的php脚本,作为index.html响应一个简单的REST请求。PHP脚本解析index.html文件的服务器副本,提取版本号并返回它。在我的SPA代码中,每次呈现新页面时,我都会对PHP脚本进行 AJAX 调用,从本地 meta标签中提取版本并比较两者。如果不同,我会向用户触发警报。
PHP脚本:

<?php
include_once('simplehtmldom_1_9/simple_html_dom.php');
header("Content-Type:application/json");
/*
    blantly stolen from: https://shareurcodes.com/blog/creating%20a%20simple%20rest%20api%20in%20php
*/

if(!empty($_GET['name']))
{
    $name=$_GET['name'];
    $price = get_meta($name);

    if(empty($price))
    {
        response(200,"META Not Found",NULL);
    }
    else
    {
        response(200,"META Found",$price);
    }   
}
else
{
    response(400,"Invalid Request",NULL);
}

function response($status,$status_message,$data)
{
    header("HTTP/1.1 ".$status);

    $response['status']=$status;
    $response['status_message']=$status_message;
    $response['content']=$data;

    $json_response = json_encode($response);
    echo $json_response;
}

function get_meta($name)
{
    $html = file_get_html('index.html');
    foreach($html->find('meta') as $e){
        if ( $e->name == $name){
            return $e->content ;
        }
    }
}
xhv8bpkk

xhv8bpkk6#

是的,在服务器端渲染中,如果你需要更新页面的一小部分,你也需要重新加载整个页面。但是在SPA中,你使用 AJAX 更新你的东西,因此不需要重新加载页面。看到你的问题,我有一些假设:
你看到你的一个组件更新了,但是其他从同一个API获取数据的组件没有更新。这里有Flux Architecture.你把你的数据放在store中,你的组件监听store的变化,每当store中的数据变化时,所有监听它的变化的组件都会更新(没有缓存场景)。
或者
您需要控制组件自动更新。
1.您可以在特定的时间间隔内向服务器请求数据
1.Websockets可以帮助您从服务器端更新组件数据。

vtwuwzda

vtwuwzda7#

使用Axios、Redux和.Net Core REST API实现了以下解决方案。x1c 0d1x
在初始配置Axios时,会在默认值中添加一个自定义头。这会导致所有客户端GET请求都包含客户端版本号(在package.json中定义)作为请求头。

axios.defaults.headers.get["x-version"] = process.env.VERSION?.toString()??"0";

在REST API中,中间件消息处理程序拦截所有HTTP请求并检查请求是否包含 x-version 头。如果是,则将 x-header 值与appsettings.json中定义的值进行检查。如果这些值不匹配,则将头 x-client-incompatable 添加到响应中。
Startup.cs中的中间件配置:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
     ...
     app.UseMiddleware<VersionMessageHandler>();
     ...
 }

VersionMessageHandler.cs

public class VersionMessageHandler 
{
    private readonly RequestDelegate _next;
    private readonly IConfiguration _configuration;

    public VersionMessageHandler(RequestDelegate next, IConfiguration configuration)
    {
        _next = next;
        _configuration = configuration;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase)) 
        {
            var version = context.Request.Headers["x-version"].ToString();
            if (string.IsNullOrEmpty(version) == false)
            {
                context.Response.OnStarting(() =>
                {
                    var compatableClientVersion = _configuration.GetValue<string>("CompatableClientVersion");
                    if (string.IsNullOrEmpty(compatableClientVersion) == false && version != compatableClientVersion)
                    {
                        context.Response.Headers.Add("x-client-incompatable", "true");
                    }

                    return Task.FromResult(0);
                });
            }
        }

        await _next(context);
    }
}

当客户端收到响应时,Axios拦截器(也定义为初始Axios配置的一部分)用于检查响应中的 x-client-compatable 头。如果找到头,则会调度redux状态更新。
Axios拦截器(Interceptors):

axios.interceptors.response.use(
    (response: AxiosResponse) => {

        // If we have the x-client-incompatable then the client is incompatable with the API.
        if(response.headers && response.headers["x-client-incompatable"]) {

            // Check if we already know before dispatching state update
            const appState: ApplicationState = store.getState();

            if (appState.compatability === undefined || appState.compatability.incompatable === false) {
                store.dispatch({ type: 'COMPATABILITY_CHECK', incompatable: response.headers["x-client-incompatable"] });
            }
        }

        return response;
    }
);

从这里开始,它只是标准的react功能-redux状态的更改会导致在应用程序的头部显示一个通知组件(用户仍然可以继续工作)。通知有一个onClick处理程序,它调用 window.location.reload(),导致客户端从服务器重新加载。
注意. CORS可能restrict response headers-如果是这样,那么您需要在API中配置CORS WithExposedHeaders(startup.cs/ConfigureServices)

services.AddCors(options =>
            {
                options.AddPolicy("AllowedDomains",
                    policy => policy
                        .WithExposedHeaders("x-client-incompatable")
                );
            });

相关问题