如何在es6中缓存胸部导入的模块?

时间:2017-12-06 13:30:28

标签: web browser-cache es6-modules

ES6模块允许我们创建一个单一的入口点,如下所示:



// main.js

import foo from 'foo';

foo()

<script src="scripts/main.js" type="module"></script>
&#13;
&#13;
&#13;

foo.js 将存储在浏览器缓存中。在我将 foo.js 的新版本推向生产之前,这是可取的。

通常的做法是添加一个具有唯一ID的查询字符串参数,以强制浏览器获取新版本的js文件( foo.js?cb = 1234

如何使用es6模块模式实现这一目标?

8 个答案:

答案 0 :(得分:1)

对于所有这一切,有一个不涉及查询字符串的解决方案。假设您的模块文件位于/modules/中。导入模块时,请使用相对模块分辨率./../,然后在服务器端重写路径以包含版本号。使用类似/modules/x.x.x/的内容,然后将路径重写为/modules/。现在,只需在第一个模块中添加以下内容,即可获得模块的全局版本号 <script type="module" src="/modules/1.1.2/foo.mjs"></script>

或者,如果您无法重写路径,则只需在开发过程中将文件放入文件夹/modules/version/中,然后将version文件夹重命名为版本号,并在发布时更新脚本标记中的路径。

答案 1 :(得分:1)

从我的角度来看,dynamic imports是这里的解决方案。

步骤1) 使用gulp或webpack创建清单文件。那里有这样的映射:

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

步骤2) 创建文件功能来解析路径

import manifest from './manifest.js';

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

export default busted;

步骤3) 使用动态导入

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

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

我在Chrome浏览器中尝试了一下,即可正常工作。在这里处理相对路径是棘手的部分。

答案 2 :(得分:0)

此刻只是一个想法,但是您应该能够使weboack将内容哈希放入所有拆分包中,并为您将该哈希写入到import语句中。我相信默认情况下它会执行第二个操作。

答案 3 :(得分:0)

用于救援的HTTP标头。使用ETag(即文件的校验和)为文件提供服务。例如,S3会执行by default。 当您尝试再次导入文件时,浏览器将请求该文件,这次将ETag附加到“ if-none-match”标头中:服务器将验证ETag是否与当前文件匹配,并发送回304 Not修改后,可节省带宽和时间,或文件的新内容(带有新的ETag)。

这样,如果您在项目中更改单个文件,则用户将不必下载所有其他模块的全部内容。最好添加一个简短的D标头,这样,如果在短时间内两次请求相同的模块,就不会有其他请求。

如果添加缓存清除(例如,通过捆绑程序添加?x = {randomNumber},或将校验和添加到每个文件名),将强制用户在每个新项目版本中下载每个必需文件的全部内容。

在两种情况下,您无论如何都要对每个文件进行请求(级联导入的文件将产生新请求,如果使用etag,则至少可以以小304结尾)。为避免这种情况,您可以使用动态导入C object

答案 4 :(得分:0)

您可以使用上一个答案指出的ETag,也可以将Last-ModifiedIf-Modified-Since结合使用。

这是一种可能的情况:

  1. 浏览器首先加载资源。服务器以Last-Modified: Sat, 28 Mar 2020 18:12:45 GMTCache-Control: max-age=60进行响应。
  2. 如果第二次在第一次请求之后60秒之前启动请求,则浏览器将从缓存中提供文件,而不会向服务器发出实际请求。
  3. 如果在60秒后启动请求,浏览器将认为缓存的文件已过时,并使用If-Modified-Since: Sat, 28 Mar 2020 18:12:45 GMT标头发送请求。服务器将检查该值,并:
    • 如果文件在上述日期之后被修改,它将在正文中对新文件发出200响应。
    • 如果在该日期之后未对文件进行 修改,则服务器将以空正文发出304“未修改”状态。

我最终完成了针对Apache服务器的设置:

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

您可以根据自己的喜好设置max-age

我们必须取消设置ETag。否则,Apache每次(it's a bug)都会始终以200 OK进行响应。此外,如果您基于修改日期使用缓存,则不需要它。

答案 5 :(得分:0)

我创建了一个 Babel plugin,它将内容哈希添加到每个模块名称(静态和动态导入)。

import foo from './js/foo.js';

import('./bar.js').then(bar => bar());

变成

import foo from './js/foo.abcd1234.js';

import('./bar.1234abcd.js').then(bar => bar());

然后您可以使用 Cache-control: immutable 让 UA(浏览器、代理等)无限期地缓存这些版本化的 URL。一些 max-age 可能更合理,具体取决于您的设置。

您可以在开发(和测试)期间使用原始源文件,然后转换和缩小用于生产的文件。

答案 6 :(得分:-1)

一个让我大吃一惊但我不会使用的解决方案,因为我不喜欢大声笑

window.version = `1.0.0`;

let { default: fu } = await import( `./bar.js?v=${ window.version }` );

使用导入的“方法”可以传递模板文字字符串。我还将其添加到窗口中,以便无论我导入js文件的深度如何都可以轻松访问它。我不喜欢它的原因是我必须使用“ await”,这意味着它必须包装在异步方法中。

答案 7 :(得分:-5)

使用相对路径对我有用:

import foo from './foo';

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

代替

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

编辑

由于该答案已被否决,因此我对其进行了更新。并非总是重新加载模块。第一次,您必须手动重新加载模块,然后浏览器(至少是Chrome)将“了解”文件已修改,然后在每次更新时重新加载文件。