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

spi*_*ers 21 browser-cache web es6-modules

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

// main.js

import foo from 'foo';

foo()
Run Code Online (Sandbox Code Playgroud)
<script src="scripts/main.js" type="module"></script>
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ric*_*lli 11

HTTP 标头来救援。使用作为文件校验和的ETag为您的文件提供服务。S3在示例中默认执行此操作。当您再次尝试导入文件时,浏览器将请求该文件,这次将 ETag 附加到“ if-none-match ”标头:服务器将验证 ETag 是否与当前文件匹配并返回一个 304 Not修改,节省带宽和时间,或文件的新内容(带有新的 ETag)。

这样,如果您更改项目中的单个文件,用户将不必下载所有其他模块的完整内容。添加一个短max-age头也是明智的,这样如果同一模块在短时间内被请求两次,就不会有额外的请求。

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

在这两种情况下,您无论如何都会对每个文件进行请求(级联上的导入文件将产生新请求,如果您使用 etags,至少可能以小 304 结束)。为避免这种情况,您可以使用动态导入e.g if (userClickedOnSomethingAndINeedToLoadSomeMoreStuff) { import('./someModule').then('...') }

  • 当然,使用“ETag”仍然会导致每个模块有 1 个 HTTP 请求,因此可能会产生大量开销 - 添加“max-age”会导致更多问题,因为模块会单独过期,这意味着在部署更新时有人可能会获得不兼容的模块版本。在文件名中使用内容校验和进行缓存清除只会强制更新已更改的模块,因此这是我所知道的最佳策略 - 并且可能是唯一可以让您永久缓存、没有 HTTP 开销和精确更新的策略。太糟糕了,我们仍然必须使用捆绑器才能获得适当的缓存。 (3认同)
  • 不过,ETag 仅定期重新检查,通常根据启发式新鲜度检查:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#heuristic_freshness_checking。如果像我一样,您正在从 NPM 存储库提供静态文件,该存储库将文件的上次修改日期设置为 1985 (https://github.com/angular/components/issues/11009),则仅每 3 检查一次 ETag到4年! (2认同)

Wan*_*ton 10

对于所有这些,有一种解决方案不涉及查询字符串。假设您的模块文件在/modules/. 使用相对模块解析./../导入模块时,然后在服务器端重写路径以包含版本号。使用类似的东西/modules/x.x.x/然后将路径重写为/modules/. 现在你可以通过包含你的第一个模块来获得模块的全局版本号 <script type="module" src="/modules/1.1.2/foo.mjs"></script>

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

  • 恕我直言,这是最好的选择。新版本只是进入一个新目录,这样旧版本和新版本都可以工作并受到支持。然后,当您决定是时候删除旧版本时。 (2认同)

b.p*_*ell 10

您可以使用导入映射来实现此目的。我至少在 Edge 中测试过它。这只是将版本号或哈希值附加到查询字符串的老技巧的一个变体。 import不会将查询字符串发送到服务器,但如果您使用导入映射,它就会发送。

<script type="importmap">
    {
      "imports": {
        "/js/mylib.js": "/js/mylib.js?v=1",
        "/js/myOtherLib.js": "/js/myOtherLib.js?v=1"
      }
    }    
</script>
Run Code Online (Sandbox Code Playgroud)

然后在你的调用代码中:

import myThing from '/js/mylib.js';
import * as lib from '/js/myOtherLib.js';
Run Code Online (Sandbox Code Playgroud)