最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

browser cache - How to handle caching of JavaScript files imported as ES6 module - Stack Overflow

programmeradmin1浏览0评论

Old situation

Previously, I used the following method to force the browser to reload my JavaScript file if there was a new version available.

<script src="common.js?version=1337"></script>
<script src="app.js?version=1337"></script>

My HTML is automatically generated (e.g. with PHP), so this is easy to automate.

New situation

Now I want to use ES6 modules and import my common code. My HTML becomes:

<script src="app.js?version=1337" type="module"></script>

And app.js contains the import:

import {foo, bar} from './common.js';

Problem

Now my question: how do I influence the caching of common.js in the new scenario?

I don't want to manually edit app.js every time I edit common.js. I also don't want to dynamically generate/pre-process any of my JavaScript files, if possible.

Old situation

Previously, I used the following method to force the browser to reload my JavaScript file if there was a new version available.

<script src="common.js?version=1337"></script>
<script src="app.js?version=1337"></script>

My HTML is automatically generated (e.g. with PHP), so this is easy to automate.

New situation

Now I want to use ES6 modules and import my common code. My HTML becomes:

<script src="app.js?version=1337" type="module"></script>

And app.js contains the import:

import {foo, bar} from './common.js';

Problem

Now my question: how do I influence the caching of common.js in the new scenario?

I don't want to manually edit app.js every time I edit common.js. I also don't want to dynamically generate/pre-process any of my JavaScript files, if possible.

Share Improve this question asked Aug 29, 2017 at 16:21 sebassebas 1,5131 gold badge15 silver badges16 bronze badges 2
  • 1 A service worker could be used to process module requests. Native ES module implementations don't handle such things, that's why bundling tools still have no alternatives in production. – Estus Flask Commented Sep 6, 2017 at 21:40
  • I wrote and answer you could be interested in at this similar question stackoverflow.com/a/56801858/210090 – Riccardo Galli Commented Jun 28, 2019 at 6:25
Add a comment  | 

3 Answers 3

Reset to default 5

You can use dynamic imports - if you're using ES6 modules (which seems to be the case) - or the Cache-Control response header if you're using any server that allows you to control your response headers through files like .htaccess.

I know this question is 5 years old, but I faced this problem yesterday and bundlers are not always available for all types of projects.

Solution 1: Dynamic Imports

Dynamic imports can be used at the top level of a module just like the regular import declaration. You just need to add the await operator and assign its value to a constant.

The following example considers that you are using PHP and that you do not wish to emit your import line with it, only the version number.

index.php

<?php
// Add your dynamic version number somewhere accessible 
// to JS (constant, element attribute, etc.).
echo '<script>const assetsVersion = "' . $assets_version . '";</script>';

app.js

// Add your import to the top-level of your module referencing
// the version number in the string.
const { MyImport } = await import("./my-import.js?v=" + assetsVersion);

This solution allows you to keep your current cache-bust technique - and also works regardless of the back-end technology used.

Solution 2: Cache-Control

This option requires you to modify your .htaccess file. Setting the no-cache response directive will make your browser validate your cached assets before actually using them (not the same as the no-store directive).

The validation is done through the If-None-Match and the If-Modified-Since request headers. The server will respond with a 304 status code if the file wasn't modified or with a 200 status code if the asset was updated - causing your browser to re-download it.

.htaccess

<IfModule mod_headers.c>
    <FilesMatch "\.(css|js)$">
        Header set Cache-Control "no-cache"
    </FilesMatch>
</IfModule>

If you're using Apache you might face issues with your server always returning 200 (due to a bug as far as I know). Removing the ETag header will probably solve it (worked for me):

<IfModule mod_headers.c>
    <FilesMatch "\.(css|js)$">
        Header set Cache-Control "no-cache"
        Header unset ETag
    </FilesMatch>
</IfModule>

This solution affects both module imports and normal scripts.

Short Version:

Just use Webpack, and you can keep doing your trick because all the javascript will be in a single file.

Long Version:

You said you don't want to pre-process your javascript files. I would reconsider that stance. Most modern web applications (and their associated frameworks like React, Angular, Vue.js) are built using some sort of preprocessor/packager that bundles all the javascript for the app into one or more files.

There are many good reasons to do this that we don't need to re-iterate in full here, but briefly, you can do type-checking (e.g. TypeScript), linting, tree-shaking, optimization, obfuscation, and compacting easily.

Bundled javascript is often smaller (by a large margin), faster, and more correct. It leads to shorter loading times and less bandwidth usage, which is particularly important when so many more than 50% of web traffic is from mobile these days.

While it might be a bit of learning curve, it's the direction our industry is going, and with good reason. The reason there isn't an easy way to include a cache-busting query parameter in ES module important is probably because the designers of the language considered it an anti-pattern.

ES6 modules can import PHP files that output valid JS. As long as you also set the proper header you should be OK.

So your index.php file would contain:

<script src="app.js.php?version=1337" type="module"></script>

And your app.js.php file would contain:

<?php
  header("Content-Type: application/javascript");
  $version = 1337; // Probably imported form some config file;
?>
import {foo, bar} from './common.js.php?version=<?=$version?>';
发布评论

评论列表(0)

  1. 暂无评论