首页 文章

加载并执行脚本的顺序

提问于
浏览
221

有很多不同的方法可以在html页面中包含JavaScript . 我知道以下选项:

  • 内联代码或从外部URI加载

  • 包含在<head>或<body>标签中[12]

  • 没有, deferasync 属性(仅限外部脚本)

  • 包含在静态源中或由其他脚本动态添加(在不同的解析状态下,使用不同的方法)

不计算来自硬盘,javascript:URIs和 onEvent -attributes [3]的浏览器脚本,已经有16个替代方法可以执行JS,我确定我忘了一些东西 .

我对执行顺序更加好奇(这可能取决于加载顺序和document order) . Is there a good (跨浏览器) reference that covers really all cases? 例如http://www.websiteoptimization.com/speed/tweak/defer/仅处理其中的6个,并且主要测试旧浏览器 .

我担心没有,这是我的具体问题:我有一些(外部)头脚本用于初始化和脚本加载 . 然后我在身体的末尾有两个静态的内联脚本 . 第一个允许脚本加载器动态地将另一个脚本元素(引用外部js)附加到正文 . 第二个静态内联脚本想要使用添加的外部脚本中的js . 它可以依赖于已执行的其他(以及为什么:-)?

2 回答

  • 11

    如果您没有动态加载脚本或将它们标记为延迟或异步,则脚本将按照页面中遇到的顺序加载 . 它是外部脚本还是内联脚本无关紧要 - 它们按照页面中遇到的顺序执行 . 外部脚本之后的内联脚本将保留,直到加载并运行之前的所有外部脚本 .

    异步脚本(无论它们如何指定为异步)以不可预测的顺序加载和运行 . 浏览器并行加载它们,可以按照自己想要的顺序自由运行它们 .

    多个异步事物之间没有可预测的顺序 . 如果需要一个可预测的订单,则必须通过从异步脚本注册加载通知并在加载适当的东西时手动排序javascript调用来编码 .

    当动态插入脚本标记时,执行顺序的行为将取决于浏览器 . 您可以在this reference article中查看Firefox的行为方式 . 简而言之,较新版本的Firefox默认动态添加脚本标记为异步,除非已设置脚本标记 .

    带有 async 的脚本标记可以在加载后立即运行 . 实际上,浏览器可能会将解析器从其正在执行的任何操作中暂停并运行该脚本 . 所以,它几乎可以随时运行 . 如果脚本被缓存,它可能几乎立即运行 . 如果脚本需要一段时间才能加载,它可能会在解析器完成后运行 . async 要记住的一件事是,它可以随时运行,而且时间不可预测 .

    带有 defer 的脚本标记将等待整个解析器完成,然后按照遇到的顺序运行标有 defer 的所有脚本 . 这允许您将几个彼此依赖的脚本标记为 defer . 它们都将被推迟到文档解析器完成之后,但它们将按照它们遇到的顺序执行,保留它们的依赖关系 . 我认为 defer 就像脚本被放入一个队列中,将在解析器完成后处理 . 从技术上讲,浏览器可能在任何时候在后台下载脚本,但是在解析器完成解析页面并解析和运行任何未标记为延迟或异步的内联脚本之后,它们才会执行或阻止解析器 .

    以下是该文章的引用:

    脚本插入的脚本在IE和WebKit中异步执行,但在Opera和4.0之前的Firefox中同步执行 .

    HTML5规范的相关部分(适用于较新的兼容浏览器)是here . 有很多关于异步行为的文章 . 显然,这个规范并没有你可能需要测试来确定的行为 .

    来自HTML5规范的引用:

    然后,必须遵循以下第一个描述情况的选项:如果元素具有src属性,并且元素具有defer属性,并且元素已标记为“解析器插入”,并且元素执行没有async属性必须将元素添加到将要执行的脚本列表的末尾当文档完成与创建元素的解析器的Document相关联的解析时 . 一旦获取算法完成,网络任务源在任务队列上放置的任务必须设置元素的“准备好被解析器执行”标志 . 解析器将处理执行脚本 . 如果元素具有src属性,并且该元素已标记为“parser-inserted”,并且该元素没有async属性该元素是创建该元素的解析器Document的挂起解析阻止脚本 . (每个文档一次只能有一个这样的脚本 . )一旦获取算法完成,网络任务源放置在任务队列上的任务必须设置元素的“准备好解析器执行”标志 . 解析器将处理执行脚本 . 如果元素没有src属性,并且元素已被标记为“解析器插入”,则创建脚本元素的HTML解析器或XML解析器的Document具有阻塞脚本的样式表元素是正在创建元素的解析器的Document的解析阻塞脚本 . (每个文档一次只能有一个这样的脚本 . )设置元素的“准备好解析器执行”标志 . 解析器将处理执行脚本 . 如果元素具有src属性,没有async属性,并且没有设置“force-async”标志元素必须添加到脚本列表的末尾,这些脚本将按顺序执行,并尽快关联在准备脚本算法开始时使用脚本元素的Document . 一旦获取算法完成,网络任务源在任务队列上放置的任务必须执行以下步骤:如果该元素现在不是脚本列表中的第一个元素,它将尽快按顺序执行在上面添加,然后将元素标记为就绪,但在不执行脚本的情况下中止这些步骤 . 执行:执行与此脚本列表中第一个脚本元素对应的脚本块,该脚本将尽快按顺序执行 . 从此脚本列表中删除第一个元素,这些脚本将尽快按顺序执行 . 如果这个将尽快按顺序执行的脚本列表仍然不为空且第一个条目已被标记为就绪,则跳回标记为执行的步骤 . 如果元素具有src属性必须将元素添加到脚本集中,这些脚本将在准备脚本算法开始时尽快执行脚本元素的Document . 一旦提取算法完成,网络任务源在任务队列上放置的任务必须执行脚本块,然后从将尽快执行的脚本集中删除该元素 . 否则,即使其他脚本已在执行,用户代理也必须立即执行脚本块 .

  • 271

    浏览器将按照找到的顺序执行脚本 . 如果调用外部脚本,它将阻止页面,直到加载并执行脚本 .

    为了测试这个事实:

    // file: test.php
    sleep(10);
    die("alert('Done!');");
    
    // HTML file:
    <script type="text/javascript" src="test.php"></script>
    

    动态添加的脚本会在附加到文档后立即执行 .

    为了测试这个事实:

    <!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> ),则会出现错误 .

    总的来说,是的,您可以包含外部脚本,然后访问它们的功能和变量,但前提是您退出当前的 <script> 标记并开始新标记 .

相关问题