web-dev-qa-db-fra.com

Comment forcer la mise à jour des pages Single Page Application (SPA)?

Dans le rendu entièrement côté serveur (non Web 2.0), le déploiement de code côté serveur mettrait directement à jour les pages côté client lors du rechargement de la page. En revanche, dans React Application basée sur une seule page, même après la mise à jour des composants React), certains clients utiliseraient l'ancienne version des composants (ils ne obtenir la nouvelle version lors du rechargement du navigateur, ce qui devrait rarement arriver) -> Si les pages sont entièrement SPA, il est possible que certains clients ne rafraîchissent les pages qu'après quelques heures.

Quelles techniques devraient être utilisées pour s'assurer que les anciennes versions des composants ne sont plus utilisées par les clients?

Mise à jour: l'API n'a pas changé, seulement React Le composant est mis à jour avec une version plus récente.

36

Vous pouvez avoir un composant React faire une requête ajax à votre serveur, lors du chargement de l'application, pour récupérer "version d'interface" . Dans l'API du serveur, vous pouvez conserver une valeur incrémentielle pour la version client. Le composant React peut stocker cette valeur sur le client (cookie/stockage local/etc). Lorsqu'il détecte un changement, il peut invoquer window.location.reload(true); qui devrait forcer le navigateur à supprimer le cache client et recharger le SPA. Ou mieux encore, informer l'utilisateur final qu'une nouvelle version sera chargée et demandez-leur s'ils souhaitent enregistrer le travail, puis le recharger, etc. Cela dépend de ce que vous voulez faire.

38
hazardous

Semblable à la réponse de Steve Taylor, mais au lieu de versionner les points de terminaison de l'API, je mettrais à jour l'application cliente de la manière suivante.

Avec chaque demande HTTP, envoyez un en-tête personnalisé, tel que:

X-Client-Version: 1.0.0

Le serveur pourrait alors intercepter cet en-tête et répondre en conséquence.

Si le serveur sait que la version du client est périmée, par exemple si la version actuelle est 1.1.0, Répondez avec un code d'état HTTP qui sera correctement géré par le client, tel que:

418 - I'm a Teapot

Le client peut alors être programmé pour réagir à une telle réponse en rafraîchissant l'application avec:

window.location.reload(true)

La prémisse sous-jacente est que le serveur connaît la dernière version du client.

MODIFIER:

Une réponse similaire est donnée ici .

8
Andy

Quelles techniques devraient être utilisées pour s'assurer que les anciennes versions des composants ne sont plus utilisées par les clients?

aujourd'hui (2018), de nombreuses applications frontales utilisent travailleurs de service . Avec lui, il est possible de gérer le cycle de vie de votre application de plusieurs manières.

Voici un premier exemple, en utilisant une notification d'interface utilisateur, en demandant à vos visiteurs d'actualiser la page Web afin d'obtenir la dernière version de l'application.

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

// ....

il existe également un moyen élégant de vérifier les mises à niveau en arrière-plan, puis de mettre à niveau l'application en silence lorsque l'utilisateur clique sur un lien interne. Cette méthode est présentée sur ach.codes et discutée sur ce thread ainsi

4
LIIT

Vous pouvez envoyer la version de l'application avec chaque réponse de n'importe quel point de terminaison de votre API. Ainsi, lorsque l'application fait une demande d'API, vous pouvez facilement vérifier qu'il existe une nouvelle version et vous avez besoin d'un rechargement dur. Si la version de la réponse de l'API est plus récente que celle stockée dans localStorage, définissez window.updateRequired = true. Et vous pouvez avoir le composant react suivant qui enveloppe react-router's Link:

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;

Et utilisez-le à la place de Link de react-router dans toute l'application. Donc, chaque fois qu'il y a une mise à jour et que l'utilisateur accède à une autre page, il y aura un rechargement dur et l'utilisateur obtiendra la dernière version de l'application.

Vous pouvez également afficher une fenêtre contextuelle disant: "Il y a une mise à jour, cliquez sur [ici] pour l'activer." si vous n'avez qu'une seule page ou si vos utilisateurs naviguent très rarement. Ou rechargez simplement l'application sans demander. Cela dépend de votre application et de vos utilisateurs.

4
Yan Takushevich

Oui dans le rendu côté serveur si vous devez mettre à jour une petite partie de la page, vous devez également recharger la page entière. Mais dans les SPA, vous mettez à jour vos fichiers en utilisant ajax, donc pas besoin de recharger la page. En voyant votre problème, j'ai quelques hypothèses:

Vous voyez que l'un de vos composants a été mis à jour, mais que d'autres composants obtenant des données de la même API n'ont pas été mis à jour. Voici Flux Architecture. où vous avez vos données en magasin et votre composant écoutent les modifications du magasin, chaque fois que les données de votre magasin changent tous vos composants en écoutant son changement seront mis à jour (pas de scène de mise en cache).

Ou

Vous devez contrôler votre composant pour qu'il soit mis à jour automatiquement. Pour ça

  1. Vous pouvez demander des données à votre serveur à des intervalles spécifiques
  2. Websockets peut vous aider à mettre à jour les données des composants du serveur.
1
Kishore Barik

Je sais que c'est un vieux fil, et les travailleurs des services sont probablement la meilleure réponse. Mais j'ai une approche simple qui semble fonctionner:

J'ai ajouté une balise META à mon fichier "index.html":

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

J'ai ensuite un script php très simple dans le même dossier que l'index.html qui répond à une simple demande REST. Le script PHP analyse la copie du serveur) du fichier index.html, extrait le numéro de version et le renvoie. Dans mon code SPA, chaque fois qu'une nouvelle page est rendue, je lance un appel ajax au script PHP, extrayez la version de la balise meta locale et comparer les deux. Si différent je déclenche une alerte à l'utilisateur.

Script 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