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?
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('...') }
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.
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.
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.
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';
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:
Last-Modified: Sat, 28 Mar 2020 18:12:45 GMT
et Cache-Control: max-age=60
.If-Modified-Since: Sat, 28 Mar 2020 18:12:45 GMT
entête. Le serveur vérifiera cette valeur et: 200
réponse avec le nouveau fichier dans le corps.304
é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.