web-dev-qa-db-ja.com

シングルページアプリケーション(SPA)ページを強制的に更新する方法は?

完全なサーバー側ベースのレンダリング(非Web 2.0)では、サーバー側のコードをデプロイすると、ページのリロード時にクライアント側のページが直接更新されます。対照的に、Reactベースの単一ページアプリケーションでは、Reactコンポーネントが更新された後でも、一部のクライアントは古いバージョンのコンポーネントを使用します。ブラウザのリロード時に新しいバージョンを取得しますが、これはめったに起こりません)->ページが完全にSPAである場合、一部のクライアントは数時間後にのみページを更新する可能性があります。

古いコンポーネントバージョンがクライアントによって使用されないようにするために、どのような手法を使用する必要がありますか?

更新:APIは変更されず、Reactコンポーネントが新しいバージョンで更新されます。

36

Reactコンポーネントは、アプリケーションのロード時にサーバーにajaxリクエストを作成して、 "インターフェースバージョン"。サーバーAPIでは、クライアントバージョンの増分値を保持できます。Reactコンポーネントは、この値をクライアントに保存できます(cookie/local storage/etc)。変更すると、window.location.reload(true);を呼び出して、 ブラウザにクライアントキャッシュを破棄させる を実行し、SPAをリロードすることができます。作業を保存してからリロードするかどうかを尋ねます。何をしたいかによって異なります。

38
hazardous

Steve Taylorの答えに似ていますが、APIエンドポイントをバージョン管理する代わりに、次の方法でクライアントアプリをバージョン管理します。

各HTTP要求で、次のようなカスタムヘッダーを送信します。

_X-Client-Version: 1.0.0_

サーバーは、そのようなヘッダーをインターセプトし、それに応じて応答することができます。

サーバーがクライアントのバージョンが古いことを認識している場合、たとえば現在のバージョンが_1.1.0_である場合、次のようにクライアントによって適切に処理されるHTTPステータスコードで応答します。

_418 - I'm a Teapot_

クライアントは、次のようにアプリを更新することで、そのような応答に反応するようにプログラムできます

window.location.reload(true)

基本的な前提は、サーバーが最新のクライアントバージョンを認識していることです。

編集:

同様の答えが here で与えられます。

8
Andy

古いコンポーネントバージョンがクライアントによって使用されないようにするために、どのような手法を使用する必要がありますか?

今日(2018)、多くのフロントアプリは サービスワーカー を使用しています。これにより、いくつかの方法でアプリのライフサイクルを管理できます。

最初の例は、UI通知を使用して、最新のアプリケーションバージョンを取得するためにWebページを更新するよう訪問者に要求します。

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 でも説明されています

4
LIIT

APIの任意のエンドポイントからのすべての応答でアプリのバージョンを送信できます。アプリが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;

そして、アプリ全体で反応ルーターのLinkの代わりに使用します。そのため、更新があり、ユーザーが別のページに移動するたびに、ハードリロードが発生し、ユーザーはアプリの最新バージョンを取得します。

また、「アップデートがあります。[ここ]をクリックして有効にしてください」というポップアップを表示することもできます。ページが1つしかない場合や、ユーザーがめったに移動しない場合。または、確認せずにアプリをリロードします。アプリとユーザーに依存します。

4
Yan Takushevich

ページの一部を更新する必要がある場合、ページ全体をリロードする必要がある場合は、サーバー側のレンダリングで可能です。ただし、SPAでは、ajaxを使用してアイテムを更新するため、ページをリロードする必要はありません。あなたの問題を見て、いくつかの仮定があります:

コンポーネントの1つが更新されましたが、同じAPIからデータを取得している他のコンポーネントは更新されませんでした。ここにFlux Architectureがあります。ストアにデータがあり、コンポーネントがストアの変更をリッスンしている場合、ストアのデータが変更されるたびに、変更をリッスンするすべてのコンポーネントが更新されます(キャッシュのシーンはありません)。

または

コンポーネントが自動的に更新されるように制御する必要があります。そのために

  1. 特定の間隔でデータをサーバーにリクエストできます
  2. Websocketsは、サーバーからのコンポーネントデータの更新に役立ちます。
1
Kishore Barik

これは古いスレッドであり、おそらくサービスワーカーが最善の答えであることは知っています。しかし、私は動作するように見える簡単なアプローチを持っています:

「index.html」ファイルにメタタグを追加しました。

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

次に、単純なREST要求に応答するindex.htmlと同じフォルダーに非常に単純なphp scripがあります。PHPスクリプトはサーバーのコピーを解析しますindex.htmlファイルのバージョン番号を抽出して返します。SPAコードでは、新しいページがレンダリングされるたびにPHPスクリプトへのajax呼び出しを行い、バージョンを抽出します。ローカルメタタグと2つを比較します。異なる場合は、ユーザーにアラートをトリガーします。

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