使用CDN或外部域在内联脚本上执行脚本文件在HTML注入时

时间:2016-01-24 14:12:17

标签: javascript dom asynchronous cdn deferred-execution

我在向已经加载的DOM中注入HTML时出现问题,其中在下载脚本文件之后加载了内联javascript。据我所知,这不应该是异步的,并且内联脚本应该在脚本文件之后执行。如果域名与调用页面相同,则此方法有效,但使用CDN甚至子域也可以执行相同的操作。我是否应该做些什么来改变我如何称呼这些?我发誓之前有这个工作,因为我有一个星期的CDN,但也许我从未发现过这个问题。

控制台

Loading Inline Script
VM1400:3 Uncaught TypeError: Cannot read property 'init' of undefined(anonymous function) 
app.members.event.js?v=204&_=1453644424985:5 Loading Script File
app.members.event.js?v=204&_=1453644424985:71 Finished Script File

的Javascript

<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
<script type="text/javascript">
console.log('Loading Inline Script');
    app.viewModel.members.event.init();
console.log('Finished Inline Script');

5 个答案:

答案 0 :(得分:3)

一种方法是使用jquery's getScript()功能。

但最好是,您可以使用本机javascript加载脚本文件,然后运行内联脚本。

也许我没有清楚地理解这个问题。

编辑: 这是关于脚本元素的HTML5规范的引用。

  

如果元素具有src内容属性,请运行以下子步骤:

     

让src为元素src属性的值。

     

如果src是空字符串,则将任务排队以触发名为的简单事件   元素出错,并中止这些步骤。

     

相对于元素解析src。

     

如果上一步失败,请将任务排队以触发一个名为的简单事件   元素出错,并中止这些步骤。

     

对获得的绝对URL进行可能启用CORS的提取,   模式是元素交叉原点的当前状态   content属性,原点是脚本元素的来源   文档,以及设置为污染的默认原始行为。

     

以这种方式获得的资源可以是CORS同源   或CORS-交叉起源。这只会影响错误报告的发生方式。

     

出于性能原因,用户代理可能会开始获取脚本(如   一旦设置了src属性,相反,在   希望元素将被插入到文档中(并且   crossorigin属性在此期间不会改变值)。无论哪种方式,   一旦元素插入到文档中,负载必须具有   按照此步骤中的说明开始。如果UA执行此类操作   预取,但元素永远不会插入文档中,或者   src属性是动态更改的,或者是crossorigin属性   动态更改,然后用户代理将不执行脚本   如此获得,并且提取过程将是有效的   浪费了。

     

然后,以下第一个描述情况的选项   必须遵守:

     

如果元素具有src属性,则该元素具有延迟   属性,该元素已被标记为&#34;解析器插入&#34;,和   元素没有async属性必须添加元素   到文档执行的脚本列表的末尾   已完成与解析器的Document关联的解析   创建了元素。

     

网络任务源在任务队列上放置一次的任务   提取算法已完成,必须将元素设置为准备好   解析器执行&#34;旗。解析器将处理执行脚本。

     

如果元素具有src属性,则该元素已被标记   as&#34;解析器插入&#34;,并且该元素没有async属性   该元素是Document of的待处理解析阻塞脚本   创建元素的解析器。 (只能有一个这样的   每个文档一次脚本。)

     

网络任务源在任务队列上放置一次的任务   提取算法已完成,必须将元素设置为准备好   解析器执行&#34;旗。解析器将处理执行脚本。

     

如果元素没有src属性,则该元素已经存在   标记为&#34;解析器插入&#34;,以及创建的解析器   script是一个XML解析器,或者是一个脚本嵌套的HTML解析器   level不大于1,以及HTML解析器的Document或   创建脚本元素的XML解析器有一个样式表   阻塞脚本该元素是暂挂的解析阻塞脚本   创建元素的解析器的Document。 (只有   一次是每个文档一个这样的脚本。)

     

设置元素&#34;准备解析器执行&#34;旗。解析器会   处理执行脚本。

     

如果元素具有src属性,则没有async属性,   并且没有&#34; force-async&#34;标志集必须添加元素   到将尽快按顺序执行的脚本列表的末尾   尽可能与脚本元素的文档相关联   准备脚本算法的时间。

     

网络任务源在任务队列上放置一次的任务   提取算法已完成,必须执行以下步骤:

     

如果元素现在不是脚本列表中的第一个元素   将尽快按顺序执行   在上面,然后将元素标记为就绪,但不中止这些步骤   正在执行脚本。

     

执行:执行与第一个脚本对应的脚本块   此列表中的元素将尽快按顺序执行   可能的。

     

从将要执行的脚本列表中删除第一个元素   为了尽快。

     

如果这个脚本列表将尽快按顺序执行   仍然不是空的,第一个条目已被标记为   准备就绪,然后跳回标记为执行的步骤。

     

如果元素具有src属性,则必须将元素添加到   一组将尽快执行Document的脚本   编写脚本算法时的脚本元素   启动。

     

网络任务源在任务队列上放置一次的任务   提取算法已完成必须执行脚本块和   然后从将执行的脚本集中删除该元素   尽快。

     

否则用户代理必须立即执行脚本块,   即使其他脚本已经在执行。获取外部   脚本必须延迟元素文档的加载事件,直到   资源一旦由网络任务源排队的任务   已被提取(已在上面定义)已经运行。

从此我认为你的&#34;外部&#34;在内联脚本块之后加载文件。因此我会使用&#34; getScript()&#34;来自jquery的函数,以确保在内联脚本块之前加载脚本。

答案 1 :(得分:0)

我有两个理论:

  1. 可能外部脚本中有一些东西会延迟app.viewModel.members对象的创建(超时或需要一段时间才能完成的事件处理程序)。通过在内联脚本中设置长超时(f.i. 5000 + ms),然后检查模型对象是否存在,可以轻松测试这一点。

  2. 加载相同的原始脚本时会发生一些时髦的事情。

  3. 在这种情况下,您可以尝试通过执行以下操作来延迟执行内联脚本:

    <script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function(event) {
        app.viewModel.members.event.init();
    });
    </script>
    

    或者只是将内联代码放在外部.js文件中,并使用'deferred'标志调用它:

    <script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
    <script type="text/javascript" src="{link-to-external-js-file}" defer></script>
    

答案 2 :(得分:0)

这是注射场景中的常见问题。它是由于脚本可用性的可变延迟以及各种浏览器上的并行和不同实现而发生的。

有3个选项,具体取决于源代码是否可用于编辑,以及脚本文件之间是否存在2个以上的依赖项。

选项1.在脚本标记中使用defer属性

如果两个脚本都是远程的(即不是内联的)

,则可以使用此选项

&#34;推迟&#34;向浏览器指示在解析文档(从MDN引用)之后脚本必须执行。这仅适用于具有&#34; src&#34;的远程(非内联)脚本。属性。

https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer

您可以像下面一样使用它。

主要浏览器支持Defer,我在Chrome,Firefox,Tizen上的Webkit和Safari上进行了验证:

https://developer.mozilla.org/en/docs/Web/HTML/Element/script#Browser_compatibility

提供上述案例的具体例子,请参阅下文。请注意,以下内容已在Firefox,Chrome,IE11,iPhone上的Safari和Tizen上的Webkit上进行了验证。

案例1:

许多Javascript文件 - 全部独立:

如果没有依赖关系,那么&#34;推迟&#34;属性允许快速加载HTML。下载后脚本会接管,没有问题(假设onload等都要小心)。

案例2:

两个javascript文件test1.js和test2.js - 一个依赖于另一个:

如果test2.js依赖于test1.js的加载,则test2.js的脚本标记仅为&#34;&#34;应该有defer属性。

此用法显示在

http://www.gpupowered.org/loadtest/2_defer.html

中显示不正确的用法

http://www.gpupowered.org/loadtest/no_defer.html(两个脚本都没有延迟标记 - 这会失败) http://www.gpupowered.org/loadtest/all_defer.html(两个脚本都有延迟标记 - 这也失败了)

不起作用的异步使用是,

http://gpupowered.org/loadtest/2_async.html(这失败了)

&#34;推迟&#34;不满足需求?

如果功能分为几个JS文件(比如说n)和所有&#34; n-1&#34;需要先下载&#34; n&#34;该文件可以开始处理一些变量,即使&#34;延迟&#34;属性可能出现在所有脚本源标记上,因为它们的接收顺序是不确定的,所以它变得无关紧要。

有关延迟的更多背景信息以及延迟加载的各种选项(不包括多延迟案例) http://www.html5rocks.com/en/tutorials/speed/script-loading/

https://developer.mozilla.org/en/docs/Web/HTML/Element/script

选项2:使用状态变量

如果可以将两个其他状态变量添加到两个javascript源文件中,则可以使用此选项。

该方法依赖于依赖js文件中的命名变量,以及使用依赖文件的js文件中的命名函数。如果在用户文件尝试访问其功能时未加载相关文件,它将退出并在真正加载时将被回调。

这在以下html文件中进行了演示。

http://gpupowered.org/loadtest/variable.html(正常工作)

如果加载需要重复发生(即加载多个同名文件等),则此选项不起作用。

选项3:本机脚本加载器

在这种情况下,有多个javascript文件彼此之间存在依赖关系。

对于使用延迟或异步或其他规范提供的标记的情况,没有解决方案。对于我在gpupowered.org的远程实验室中使用的用例,我必须使用XMLHttpRequest实现我自己的本机脚本加载器,其源代码在下面的链接中提供。这使用工作线程,因为我拥有的一些纹理相当大。回调函数可用于根据应用程序需要实现依赖关系逻辑。例如,保持所有已加载脚本的计数,然后触发完全执行等。

https://github.com/prabindh/gpupowered.gl/blob/master/worker/worker_object_loader.js

jquery脚本加载器也使用HTTP请求,但我还没有检查它是否使用worker进行加载。 https://api.jquery.com/jquery.getscript/

答案 3 :(得分:0)

使用此:

<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204&onload=onloadCallback"></script

<script type="text/javascript">function onloadCallback(){
app.viewModel.members.event.init();}</script>

答案 4 :(得分:0)

function onloadCallback(){
    app.viewModel.members.event.init();
}