简体   繁体   English

如何处理作为 ES6 模块导入的 JavaScript 文件的缓存

[英]How to handle caching of JavaScript files imported as ES6 module

Old situation旧情

Previously, I used the following method to force the browser to reload my JavaScript file if there was a new version available.以前,如果有可用的新版本,我使用以下方法强制浏览器重新加载我的 JavaScript 文件。

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

My HTML is automatically generated (eg with PHP), so this is easy to automate.我的 HTML 是自动生成的(例如使用 PHP),因此很容易实现自动化。

New situation新形势

Now I want to use ES6 modules and import my common code.现在我想使用 ES6 模块并导入我的常用代码。 My HTML becomes:我的 HTML 变为:

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

And app.js contains the import: app.js包含导入:

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

Problem问题

Now my question: how do I influence the caching of common.js in the new scenario?现在我的问题是:在新场景中如何影响common.js的缓存?

I don't want to manually edit app.js every time I edit common.js .我不想每次编辑common.js时都手动编辑app.js I also don't want to dynamically generate/pre-process any of my JavaScript files, if possible.如果可能的话,我也不想动态生成/预处理我的任何 JavaScript 文件。

ES6 modules can import PHP files that output valid JS. ES6模块可以导入输出有效JS的PHP文件。 As long as you also set the proper header you should be OK. 只要你也设置了正确的标题,你应该没问题。

So your index.php file would contain: 所以你的index.php文件将包含:

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

And your app.js.php file would contain: 你的app.js.php文件将包含:

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

Short Version:简洁版本:

Just use Webpack , and you can keep doing your trick because all the javascript will be in a single file.只需使用Webpack ,您就可以继续使用您的技巧,因为所有的 javascript 都将在一个文件中。

Long Version:长版:

You said you don't want to pre-process your javascript files.你说你不想预处理你的 javascript 文件。 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.大多数现代 Web 应用程序(及其相关框架,如 React、Angular、Vue.js)都是使用某种预处理器/打包器构建的,这些预处理器/打包器将应用程序的所有 javascript 捆绑到一个或多个文件中。

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 (eg TypeScript), linting, tree-shaking, optimization, obfuscation, and compacting easily.这样做有很多很好的理由,我们不需要在这里全面重复,但简而言之,您可以轻松地进行类型检查(例如 TypeScript)、linting、摇树、优化、混淆和压缩。

Bundled javascript is often smaller (by a large margin), faster, and more correct.捆绑的 javascript 通常更小(大幅度)、更快、更正确。 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.它会导致更短的加载时间和更少的带宽使用,这在如今超过 50% 的网络流量来自移动设备时尤为重要。

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.没有一种简单的方法在 ES 模块中包含缓存破坏查询参数重要的原因可能是因为该语言的设计者认为它是一种反模式。

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 .您可以使用动态导入- 如果您使用的是 ES6 模块(似乎是这种情况) - 或者如果您使用任何允许您通过 .htaccess 等文件控制响应标头的服务器,则可以使用缓存控制响应.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.我知道这个问题已有 5 年历史,但我昨天遇到了这个问题,捆绑器并不总是适用于所有类型的项目。

Solution 1: Dynamic Imports解决方案 1:动态导入

Dynamic imports can be used at the top level of a module just like the regular import declaration.动态导入可以在模块的顶层使用,就像常规import声明一样。 You just need to add the await operator and assign its value to a constant.您只需要添加await运算符并将其值分配给常量。

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.以下示例考虑您正在使用 PHP 并且您不希望使用它发出您的导入行,只希望版本号。

index.php 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应用程序.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解决方案 2:缓存控制

This option requires you to modify your .htaccess file.此选项要求您修改.htaccess文件。 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).设置no-cache响应指令将使您的浏览器在实际使用它们之前验证您的缓存资产(与no-store指令不同)。

The validation is done through the If-None-Match and the If-Modified-Since request headers.验证是通过If-None-MatchIf-Modified-Since请求标头完成的。 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.如果文件未被修改,服务器将响应 304 状态代码;如果资产已更新,服务器将响应 200 状态代码 - 导致您的浏览器重新下载它。

.htaccess .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).如果您使用的是 Apache,您可能会遇到服务器总是返回 200 的问题(据我所知是由于错误)。 Removing the ETag header will probably solve it (worked for me):删除ETag header 可能会解决它(对我有用):

<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.此解决方案会影响模块导入和普通脚本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM