加载和执行脚本的顺序
在html页面中包含JavaScript的方式有很多种。 我知道以下选项:
- 内联代码或从外部URI加载
- 包含在<head>或<body>标记中[ 1,2 ]
- 没有,
defer
或async
属性(只有外部脚本) - 包含在静态源文件中或由其他脚本dynamic添加(在不同的分析状态下,使用不同的方法)
从硬盘,javascript:URIs和onEvent
-attributes [ 3 ]不计算的browserscripts,已经有16个替代品来执行JS,我相信我忘了一些东西。
我不太关心快速(并行)加载,我对执行顺序更加好奇(可能取决于加载顺序和文档顺序 )。 是否有一个很好的 (跨浏览器) 参考涵盖所有的情况? 例如http://www.websiteoptimization.com/speed/tweak/defer/只处理其中的6个,并testing大多数旧的浏览器。
因为我担心没有,我的具体问题是:我有一些(外部)头文件用于初始化和脚本加载。 然后,我有两个静态的内联脚本在本体的末尾。 第一个让脚本加载器dynamic地将另一个脚本元素(引用外部js)附加到主体。 第二个静态内联脚本想要使用添加的外部脚本中的js。 它可以依靠其他已被执行(以及为什么:-)?
如果不是dynamic加载脚本或将它们标记为延迟或asynchronous,则脚本将按页面中遇到的顺序加载。 无论是外部脚本还是内联脚本都无关紧要 – 它们按页面中遇到的顺序执行。 在外部脚本之后的内联脚本被保留,直到所有外部脚本都被加载并运行。
asynchronous脚本(不pipe它们如何被指定为asynchronous)加载并以不可预知的顺序运行。 浏览器并行加载它们,并且可以按照它想要的顺序来运行它们。
在多个asynchronous事件中没有可预测的顺序。 如果需要一个可预测的顺序,那么必须通过注册来自asynchronous脚本的加载通知进行编码,并在加载适当的东西时手动sortingJavaScript调用。
当dynamic插入脚本标记时,执行顺序的行为将取决于浏览器。 您可以看到Firefox在这篇参考文章中的performance。 简而言之,较新版本的Firefox会默认使用dynamic添加的脚本标记来进行asynchronous处理,除非脚本标记已被设置。
加载后,脚本标记可以运行。 事实上,浏览器可能会暂停parsing器,然后运行该脚本。 所以,它几乎可以在任何时候运行。 如果脚本被caching了,它可能立即运行。 如果脚本需要一段时间才能加载,则可能在parsing器完成后运行。 async
记住的一点是它可以随时运行,而且这个时间是不可预测的。
带有defer
的脚本标记等待整个parsing器完成,然后按遇到的顺序运行标记为defer
所有脚本。 这允许您标记几个相互依赖的脚本作为defer
。 他们都将被推迟到文档分析器完成后,但他们将按照遇到他们的依赖关系的顺序执行。 我认为defer
就像脚本被放入队列一样,在parsing器完成后将被处理。 从技术上讲,浏览器可能随时在后台下载脚本,但是直到parsing器parsing完页面并parsing并运行任何没有标记为延迟或asynchronous的内联脚本,它们才会执行或阻止parsing器。
这篇文章的引用如下:
脚本插入的脚本在IE和WebKit中asynchronous执行,但在Opera和4.0之前的Firefox中同步执行。
HTML5规范的相关部分(适用于更新的兼容浏览器)就在这里 。 那里写了很多关于asynchronous行为的东西。 显然,这个规范不适用于那些你可能需要testing以确定的行为的旧浏览器(或不确定的浏览器)。
来自HTML5规范的引用:
然后,必须遵循以下第一个描述情况的选项:
如果元素具有src属性,并且元素具有defer属性,并且该元素已被标记为“parsing器插入的”,并且该元素没有async属性该元素必须被添加到列表的末尾当文档完成与创build该元素的parsing器的Document相关联的parsing时将执行的脚本。
一旦获取algorithm完成,networking任务源放置在任务队列上的任务必须设置元素的“准备好parsing器执行”标志。 parsing器将处理执行脚本。
如果元素具有src属性,并且该元素已被标记为“parsing器插入的”,并且该元素没有async属性该元素是创build元素的parsing器的Document的暂挂parsing阻止脚本。 (每个文档一次只能有一个这样的脚本。)
一旦获取algorithm完成,networking任务源放置在任务队列上的任务必须设置元素的“准备好parsing器执行”标志。 parsing器将处理执行脚本。
如果元素没有src属性,并且该元素已被标记为“parsing器插入”,并且创build脚本元素的HTMLparsing器或XMLparsing器的Document具有阻止脚本的样式表元素是等待创build元素的parsing器的Document的parsing阻塞脚本。 (每个文档一次只能有一个这样的脚本。)
设置元素的“准备好parsing器执行”标志。 parsing器将处理执行脚本。
如果元素具有src属性,没有async属性,并且没有设置“force-async”标志 ,则必须将该元素添加到脚本列表的末尾,这些脚本将尽快按顺序执行与脚本元素的Document在准备脚本algorithm时开始。
一旦获取algorithm完成,networking任务源放置在任务队列上的任务必须执行以下步骤:
如果该元素现在不是脚本列表中的第一个元素,它将按照上面所添加的顺序尽快执行,则将该元素标记为就绪,但不执行脚本而中止这些步骤。
执行:执行脚本列表中对应于第一个脚本元素的脚本块,这些脚本将尽快执行。
从这个将尽快执行的脚本列表中删除第一个元素。
如果尽快按顺序执行的脚本列表仍然不是空的,并且第一个条目已经被标记为准备就绪,那么跳回到标有执行的步骤。
如果元素具有src属性 ,则必须将该元素添加到在准备脚本algorithm开始时脚本元素的Document尽快执行的一组脚本中。
一旦获取algorithm完成,networking任务源放置在任务队列上的任务必须执行脚本块,然后从将尽快执行的脚本集中移除该元素。
否则 ,即使其他脚本已经在执行,用户代理也必须立即执行脚本块。
浏览器将按照发现它们的顺序执行脚本。 如果你调用一个外部脚本,它将阻塞页面,直到脚本被加载和执行。
为了testing这个事实:
// file: test.php sleep(10); die("alert('Done!');"); // HTML file: <script type="text/javascript" src="test.php"></script>
dynamic添加的脚本在追加到文档后立即执行。
为了testing这个事实:
<!DOCTYPE HTML> <html> <head> <title>Test</title> </head> <body> <script type="text/javascript"> var s = document.createElement('script'); s.type = "text/javascript"; s.src = "link.js"; // file contains alert("hello!"); document.body.appendChild(s); alert("appended"); </script> <script type="text/javascript"> alert("final"); </script> </body> </html>
警报顺序是“附加” – >“你好!” – >“最后”
如果在一个脚本中,你试图访问一个尚未到达的元素(例如: <script>do something with #blah</script><div id="blah"></div>
),那么你将得到错误。
总体而言,是的,你可以包含外部脚本,然后访问它们的函数和variables,但只有当你退出当前的<script>
标签并开始一个新的<script>
。
var scriptMap =["scriptUrl1","scriptUrl2", "scriptUrl3"]; var order = 0; function loadScriptInOrder(){ if(order == scriptMap.length) { return; } var JSLink = scriptMap[order]; var JSElement = document.createElement('script'); JSElement.src = JSLink; JSElement.onload = callback; document.getElementsByTagName('body')[0].appendChild(JSElement); function callback(){ order++; loadScriptInOrder(); } }; loadScriptInOrder();