【问题标题】:Load ES6 Module dependencies before and after Plain JS Script在普通 JS 脚本之前和之后加载 ES6 模块依赖项
【发布时间】:2019-09-27 20:51:31
【问题描述】:

我有一个纯 JS 脚本 parser.js(从工具生成),它依赖于 ES6 模块 lexer.js 中定义的变量。在我的 ES6 模块中,我已经将变量导出到 window 对象,因此可以从 parser.js 访问它们。但是,我需要在运行脚本之前以某种方式运行该 ES6 模块。而且似乎没有任何方法可以这样做。

尝试 1:在包含我的脚本之前尝试同步加载 ES6 模块

我在我的 HTML 中尝试过这样的事情。

    <script src="lexer.js" type="module"></script>
    <script src="parser.js"></script>

但它似乎没有按顺序运行。 lexer.jsparser.js 之后运行

尝试 2:尝试在 ES6 模块中同步加载脚本

我尝试像这样围绕我的解析器脚本创建一个包装器 ES6 模块

// use import to run the module and load variables into the window
import { lexer } from './lexer.js';

// load parser script synchronously
var req = new XMLHttpRequest();
req.open('GET', 'parser.js', false);
req.send(null);
eval(req.responseText);

但是,似乎同步 XMLHttpRequests 已被弃用并且不再起作用(编辑:实际上它们确实如此,请参阅下面的my answer),而且我找不到任何其他同步加载脚本的方法。总的来说,我会说 ES6 模块系统和旧的 javascript 包含系统之间的不兼容,实在令人沮丧。

附:作为参考,我使用的代码生成工具是Nearley grammar compiler,它允许我从语法中引用我的词法分析器,并生成一个普通的JS解析器。

编辑:@yong-quan 提出了一个简洁的解决方案,只需将defer 放在脚本包含标签中,例如

    <script src="lexer.js" type="module"></script>
    <script src="parser.js" defer></script>

看起来这只是将parser.js 的执行推迟到最后。但是,我没有提到我实际上有一个名为 interpreter.js 的 ES6 模块,它需要在 parser.js 之后调用。很抱歉没有早点提到,我认为任何对我的第一个问题有效的解决方案也可以解决我的第二个问题。我修正了标题以澄清我需要 ES6 模块在 之前和之后 我的纯 JS 脚本运行。本质上,我需要将这个纯 JS 脚本集成到我的模块依赖图中。

EDIT2:我错了,延迟解决方案有效。请参阅下面的@Aviad 或我自己的答案

【问题讨论】:

  • 你试过&lt;script src="parser.js" defer&gt;&lt;/script&gt;吗?
  • 哇,成功了,你是救生员!如果您将评论作为答案,我会接受:)
  • 实际上等等,我不确定这是否适合我的需要,因为我实际上有一个 ES6 模块必须在 parser.js 之后运行,称为 interpreter.js。我没有在问题中提到它,因为我认为第一个问题的任何解决方案都可以解决第二个问题,但我想情况并非如此。我将在我的问题中包括最后一部分。感谢您的建议
  • 如果 parser.js 在 html 中出现在其他两个脚本之前,那么您可以推迟解析器和解释器(它将按此顺序执行),没有 defer 的词法分析器将是第一个。
  • 你是对的,@aviad 提出了同样的建议。事实上,我只是在所有脚本中添加了defer,包括lexer.js,它仍然以正确的顺序执行。很奇怪,但对我有用:)

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

我认为这里的好做法是为此创建某种下载管理器(例如通过使用 webpack 块加载/动态导入)

另一种选择是使用defer 属性。 请注意,defer 表示脚本按照它们遇到的顺序运行,因此如果顺序正确,您可以假设在调用interpreter.js 时会加载parser.js

链接:

【讨论】:

  • 哇,你说得对,我在所有脚本中添加了defer,它开始以正确的顺序运行它们。很奇怪,因为没有defer,它仍然应该按照遇到的顺序运行它们,但是这解决了我的问题,谢谢! (而且我肯定会考虑在未来转向 webpack)
  • w/o defer 你会有一个竞争条件
  • 为什么会有所不同,将defer 放在所有脚本的前面而不是一个都没有?
  • @woojoo666 假设您的页面获取 3 个脚本:A、B 和 C(它们也按此顺序出现)。现在,让我们假设由于某种原因(大小、带宽等)B 将被“更快”获取。现在,可以声称订单仍然存在 - 因此 A 将在 B 之前被获取并执行。但是!由于 HTTP 1.1 的持久连接,浏览器可以触发多个请求来获取资源,这可能导致脚本执行不按“遇到顺序”
  • 那不违反规范吗?至少对于非 ES6 脚本,如果脚本没有按顺序执行,那么很多页面都会中断。对我来说,问题似乎在于 ES6 模块不遵循相同的顺序(除非您添加 defer),这使得将旧脚本混入模块系统变得如此困难
【解决方案2】:

所以我使用@Aviad 的solution(在上面发布)解决了这个问题,并将defer 添加到我的所有脚本中,例如:

    <script src="lexer.js" type="module" defer></script>
    <script src="parser.js" defer></script>
    <script src="interpreter.js" type="module" defer></script>

它开始以正确的顺序加载它们。整洁的!虽然我认为重要的是要注意,我还没有找到 ES6 模块加载顺序的实际规范,所以似乎无法保证加载顺序(除了一个模块依赖于另一个模块的情况,在它将首先加载依赖项)。因此,虽然 defer 技巧目前有效,但我认为它可能会在未来失效。

我还想提一下,事实证明我的 XMLHttpRequest 同步脚本加载确实有效,我只是遇到了一些范围问题。我不得不从window 的范围内调用eval,因为通常parser.js 在HTML 中作为&lt;script&gt; 标记加载,所以它期望范围为window,所以我需要eval()模仿它,像这样:

// load parser script synchronously
var req = new XMLHttpRequest();
req.open('GET', 'parser.js', false);
req.send(null);
eval.call(window, req.responseText);

所以我想如果我需要的话,我有一个备用方案。我也在考虑手动将parser.js 转换为 ES6 模块,但这省去了我每次重新生成它时转换它的麻烦。感谢@Aviad 的解决方案!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-16
    • 2021-05-26
    • 1970-01-01
    • 2012-10-31
    • 2018-03-17
    • 1970-01-01
    • 2023-04-04
    • 2020-01-27
    相关资源
    最近更新 更多