web-dev-qa-db-fra.com

Comment mettre en cache les modules importés en cache dans es6?

Les modules ES6 nous permettent de créer un point d'entrée unique comme ceci:

// main.js

import foo from 'foo';

foo()
<script src="scripts/main.js" type="module"></script>

foo.js sera stocké dans le cache du navigateur. Ceci est souhaitable jusqu'à ce que je pousse une nouvelle version de foo.js en production.

Il est courant d'ajouter un paramètre de chaîne de requête avec un identifiant unique pour forcer le navigateur à récupérer une nouvelle version d'un fichier js (foo.js? Cb = 1234)

Comment cela peut-il être réalisé en utilisant le modèle de module es6?

25
spinners

Les en-têtes HTTP à la rescousse. Servez vos fichiers avec un ETag qui est la somme de contrôle du fichier. S3 fait cela par défaut par exemple. Lorsque vous essayez d'importer à nouveau le fichier, le navigateur demandera le fichier, en attachant cette fois l'ETag à un en-tête " if-none-match ": le serveur vérifiera si l'ETag correspond au fichier actuel et renvoyez soit un 304 non modifié, économisant la bande passante et le temps, soit le nouveau contenu du fichier (avec son nouvel ETag).

De cette façon, si vous modifiez un seul fichier dans votre projet, l'utilisateur n'aura pas à télécharger le contenu complet de tous les autres modules. Il serait judicieux d'ajouter également un en-tête court max-age, De sorte que si le même module est demandé deux fois en peu de temps, il n'y aura pas de demandes supplémentaires.

Si vous ajoutez le contournement du cache (par exemple, en ajoutant? X = {randomNumber} via un bundler, ou en ajoutant la somme de contrôle à chaque nom de fichier), vous obligerez l'utilisateur à télécharger le contenu complet de chaque fichier nécessaire à chaque nouvelle version du projet.

Dans les deux cas, vous allez quand même faire une demande pour chaque fichier (les fichiers importés en cascade produiront de nouvelles demandes, qui au moins peuvent se terminer en petits 304 si vous utilisez des étiquettes). Pour éviter cela, vous pouvez utiliser les importations dynamiques e.g if (userClickedOnSomethingAndINeedToLoadSomeMoreStuff) { import('./someModule').then('...') }

4
Riccardo Galli

De mon point de vue importations dynamiques pourrait être une solution ici.

Étape 1) Créez un fichier manifeste avec gulp ou webpack. Là, vous avez un mappage comme celui-ci:

export default {
    "/vendor/lib-a.mjs": "/vendor/lib-a-1234.mjs",
    "/vendor/lib-b.mjs": "/vendor/lib-b-1234.mjs"
};

Étape 2) Créez une fonction de fichier pour résoudre vos chemins

import manifest from './manifest.js';

const busted (file) => {
 return manifest[file];
};

export default busted;

Étape 3) Utilisez l'importation dynamique

import busted from '../busted.js';

import(busted('/vendor/lib-b.mjs'))
  .then((module) => {
    module.default();
});

Je lui donne un bref essai dans Chrome et cela fonctionne. La gestion des chemins relatifs est une partie délicate ici.

2
d-bro82

Juste une idée pour le moment, mais vous devriez pouvoir obtenir weboack pour mettre un hachage de contenu dans tous les bundles fractionnés et écrire ce hachage dans vos instructions d'importation pour vous. Je crois qu'il fait le second par défaut.

1
user11639687

Il existe une solution pour tout cela qui n'implique pas de chaîne de requête. disons que vos fichiers de module sont dans /modules/. Utiliser la résolution relative du module ./ ou ../ lors de l'importation de modules, puis réécrivez vos chemins d'accès côté serveur pour inclure le numéro de version. Utilisez quelque chose comme /modules/x.x.x/ puis réécrire le chemin vers /modules/. Vous pouvez maintenant avoir un numéro de version global pour les modules en incluant votre premier module avec <script type="module" src="/modules/1.1.2/foo.mjs"></script>

Ou si vous ne pouvez pas réécrire les chemins, placez simplement les fichiers dans le dossier /modules/version/ pendant le développement et renommer le dossier version en numéro de version et chemin de mise à jour dans la balise de script lors de la publication.

1
Wanton

L'utilisation du chemin relatif fonctionne pour moi:

import foo from './foo';

ou

import foo from './../modules/foo';

au lieu de

import foo from '/js/modules/foo';
0
Fifi

Vous pouvez utiliser ETags, comme indiqué dans une réponse précédente, ou utiliser Last-Modified En relation avec If-Modified-Since.

Voici un scénario possible:

  1. Le navigateur charge d'abord la ressource. Le serveur répond par Last-Modified: Sat, 28 Mar 2020 18:12:45 GMT et Cache-Control: max-age=60.
  2. Si la deuxième fois que la demande est lancée plus de 60 secondes après la première, le navigateur sert le fichier à partir du cache et ne fait pas de demande réelle au serveur.
  3. Si une demande est lancée après 60 secondes, le navigateur considérera le fichier mis en cache périmé et enverra la demande avec If-Modified-Since: Sat, 28 Mar 2020 18:12:45 GMT entête. Le serveur vérifiera cette valeur et:
    • Si le fichier a été modifié après cette date, il émettra un 200 réponse avec le nouveau fichier dans le corps.
    • Si le fichier n'a pas été modifié après la date, le serveur émettra un304 état "non modifié" avec corps vide.

Je me suis retrouvé avec cette configuration pour le serveur Apache:

<IfModule headers_module>
  <FilesMatch "\.(js|mjs)$">
    Header set Cache-Control "public, must-revalidate, max-age=3600"
    Header unset ETag
  </FilesMatch>
</IfModule>

Vous pouvez définir max-age à votre convenance.

Nous devons désactiver ETag. Sinon, Apache continue de répondre avec 200 OK à chaque fois ( c'est un bug ). De plus, vous n'en aurez pas besoin si vous utilisez la mise en cache en fonction de la date de modification.

0
Tigran