【问题标题】:XMLHttp request vs "html tag" resource loadingXMLHttprequest 与“html 标签”资源加载
【发布时间】:2016-04-09 14:14:31
【问题描述】:

我目前正在构建自己的小型 SPA 框架。我只是好奇,有些事情是如何在后台工作的。

假设我想从服务器动态导入一个脚本。

我目前使用的方法是在头部添加一个脚本标记,并将“async”属性设置为 true。像这样。

for (let requiredScript of requiredScripts){
  var $head = document.getElementsByTagName('head')[0];
  var $script = document.createElement('script');
  $script.src = requiredScript;
  $script.async = true;
  $script.onload = function(){
    callback();
  }

  $head.appendChild($script);
}

这在浏览器中是如何工作的?我读到浏览器会为 head 标签中的每个脚本生成一个新线程,当它们加载时,它们会执行它们。 (我也可以使用 defer 属性,但这并不重要)。

这(如果它按我预期的那样工作),很棒。语法很丑陋,但它有效。有一点缺点:我无法为这些请求显式设置标头。例如,我想为每个对受限资源的请求添加一个 JWT 令牌,但我不能,我必须使用 ajax 调用。但这不是这个问题的一部分。

所以,加载我的资源的第二种方法(还有第三种方法,ES6 导入,但我现在不会使用它)是通过 AJAX 调用加载我的资源。但是从 XMLHttp 请求的性质来看,我得到的只是一个字符串,或者可能是 JSON,而不是脚本,所以我必须调用 eval(),这既慢又不安全。

但是让我们来回答这个问题。 脚本标签中的资源调用到底是什么?它与 xmlhttp 请求(或 ajax 调用)有何不同?在解析 html 文件并评估带有 script 标签的行时,浏览器到底做了什么?我可以将 javascript 文件作为二进制文件导入,并让浏览器执行它吗? (比 eval() 更快更安全的选择)。 我想完全控制和理解我正在做的事情,但我感到很困惑,找不到关于这个话题的正确答案。

每一个解释(甚至是一个有用的链接)都会非常感激。

【问题讨论】:

  • 浏览器基本上只是做一个HTTP GET操作然后解析和解释脚本。一旦 Web 组装得到广泛实施,就可以在服务器上进行预解析。
  • 如果您想查看这些 HTTP 事务,浏览器“网络”控制台会显示这些事务。
  • “浏览器基本上只是做一个 HTTP GET 操作,然后解析和解释脚本”这正是让我感到困惑的地方。我可以自己做这个吗?我的意思是,我可以告诉浏览器使用这个“HTTP GET”,然后在响应上使用他的默认解析器和解释器吗?这对我来说似乎很愚蠢,我们所拥有的只是一个旧的、弱的 XMLHttp 请求,它使简单的事情变得复杂。
  • 你可以发出一个普通的 xhr 来获取脚本,然后eval() 它,尽管在一些关于eval() 工作原理的模糊细节方面存在一些细微差别。
  • 但是浏览器不会让你向其他域发出 xhr,但是通过 <script> 标签处理完成的隐式 GET 从其他域获取脚本内容是完全可以的。

标签: javascript html ajax resources xmlhttprequest


【解决方案1】:

脚本标签中的资源调用到底是什么?

<script> 标记只是告诉浏览器对脚本标记中的 URL 执行 http GET,然后将生成的文本发送到 Javascript 解释器进行解析和运行。您可以在浏览器调试器中查看网络选项卡,以查看它正在执行的确切 http GET 操作。

它与 xmlhttp 请求(或 ajax 调用)有何不同?

两者的初始请求都是http GET,因此根本没有区别。 xmlhttp 请求受同源安全保护。 <script> 标签不受同源保护,因此它可以从任何地方加载脚本。

在解析 html 文件并评估时,浏览器到底做了什么 有script标签的那一行?

我认为这是上面描述的(一个 http GET,然后将结果传递给 JS 解析器)。

我可以将 javascript 文件作为二进制文件导入,并制作浏览器吗? 执行吗? (比 eval() 更快更安全的选择)

不,不是真的。首先,JS 文件不是二进制文件,它是文本。对于预解析/编译的二进制 Javascript,还没有一个采用和广泛实施的标准。你可以阅读 EmscriptenAsm.js 来了解这方面的一些工作,尽管 Emscripten 确实针对将其他语言代码转换为 JS。

当您说“比eval() 更安全”时,您必须描述您要避免的问题。加载脚本标记或使用 Ajax 加载脚本然后调用 eval() 将在全局上下文中评估脚本,并具有广泛的功能来处理您的页面。

您可以通过使用 XMLHttpRequest 检索脚本文本来自己控制加载和执行(如果不是跨域请求),然后您可以通过将文本插入脚本标记或调用来自己解析和执行它eval() 就可以了。除非您想在执行之前以某种方式修改或验证脚本,否则自己手动加载确实没有任何好处。

【讨论】:

  • 感谢您的回答。所以,我想调用 eval() 并不像我想象的那么邪恶。但是,我还想知道一件事。当我将带有我的 javascript 代码的 xmlhttp responseText 放入脚本标记时,它会被浏览器的 Javascript 解释器解释,然后执行。这与调用 eval() 有何不同? eval() 是否也使用浏览器的解释器?或者我是否从将 responseText 插入到脚本标签中获得了一些好处?
  • @MatúšČongrády - 我相当怀疑 JS 引擎为<script> 标签和eval() 实现了完全独立的解析器和解释器。几乎可以肯定的是,大部分解析和解释代码都是在两者之间共享的。如果您想比较两者的性能,您必须设计一个性能测试并在多个浏览器中运行它,因为这将是特定于实现的,而不是由标准定义的。
  • @MatúšČongrády - eval()<script> 标签具有几乎相同的安全问题。你必须相信你正在运行的代码。 eval() 在执行由不受信任的来源(例如用户)提供的代码时,或者在完全不使用 eval() 的情况下,只需更智能的编码就可以清楚地编写代码时,eval() 获得了应避免的声誉。跨度>
  • @MatúšČongrády - 这回答了你的问题吗?如果是这样,您可以通过选中答案左侧的绿色复选标记向社区表明这一点。如果没有,那么请评论尚未回答的内容。
猜你喜欢
  • 2017-05-02
  • 2018-07-25
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 2014-01-28
相关资源
最近更新 更多