How do I cache bust imported modules in es6?

ぐ巨炮叔叔 提交于 2019-12-03 04:55:48

问题


ES6 modules allows us to create a single point of entry like so:

// main.js

import foo from 'foo';

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

foo.js will be stored in the browser cache. This is desirable until I push a new version of foo.js to production.

It is common practice to add a query string param with a unique id to force the browser to fetch a new version of a js file (foo.js?cb=1234)

How can this be achieved using the es6 module pattern?


回答1:


HTTP headers to the rescue. Serve your files with an ETag that is the checksum of the file. S3 does that by default at example. When you try to import the file again, the browser will request the file, this time attaching the ETag to a "if-none-match" header: the server will verify if the ETag matches the current file and send back either a 304 Not Modified, saving bandwith and time, or the new content of the file (with its new ETag).

This way if you change a single file in your project the user will not have to download the full content of every other module. It would be wise to add a short max-age header too, so that if the same module is requested twice in a short time there won't be additional requests.

If you add cache busting (e.g. appending ?x={randomNumber} through a bundler, or adding the checksum to every file name) you will force the user to download the full content of every necessary file at every new project version.

In both scenario you are going to do a request for each file anyway (the imported files on cascade will produce new requests, which at least may end in small 304 if you use etags). To avoid that you can use dynamic imports e.g if (userClickedOnSomethingAndINeedToLoadSomeMoreStuff) { import('./someModule').then('...') }




回答2:


From my point of view dynamic imports could be a solution here.

Step 1) Create a manifest file with gulp or webpack. There you have an mapping like this:

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

Step 2) Create a file function to resolve your paths

import manifest from './manifest.js';

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

export default busted;

Step 3) Use dynamic import

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

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

I give it a short try in Chrome and it works. Handling relative paths is tricky part here.




回答3:


There is one solution for all of this that doesn't involve query string. let's say your module files are in /modules/. Use relative module resolution ./ or ../ when importing modules and then rewrite your paths in server side to include version number. Use something like /modules/x.x.x/ then rewrite path to /modules/. Now you can just have global version number for modules by including your first module with <script type="module" src="/modules/1.1.2/foo.mjs"></script>

Or if you can't rewrite paths, then just put files into folder /modules/version/ during development and rename version folder to version number and update path in script tag when you publish.




回答4:


Just a thought at the moment but you should be able to get weboack to put a content hash in all the split bundles and write that hash into your import statements for you. I believe it does the second by by default.



来源:https://stackoverflow.com/questions/47675549/how-do-i-cache-bust-imported-modules-in-es6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!