... HEAD 中的 JavaScript ... 在页面上的最后一个脚本之前动态插入一个异步加载脚本标签...
我假设加载程序脚本是 inline,这意味着突出显示的位实际上是指“当前”脚本元素,即加载程序。发生这种情况是因为只有加载器脚本标记之前的 html 已被解析和解释,因此插入的脚本标记实际上仍然在head
页面底部而不是在页面底部。因此,目标脚本仅限于对前面的元素执行 DOM 操作,除非您将代码包装到 DOM 就绪回调中……这是您首先要避免的!
基本上你想加载所有 html 以便页面可见/可扫描,开始加载图像/样式表(发生在非阻塞线程中),然后加载任何 javascript。一种方法是将您的目标脚本放在页面底部,只需正确选择它们的顺序(首先是交互性,其次是增强功能,最后是第三方分析/社交媒体集成/其他任何超重的东西)并根据您的需要进行调整。从技术上讲,它仍然会阻止页面加载,但无论如何,页面底部只剩下脚本(并且由于它们位于底部,因此您可以在加载 DOM 后立即直接操作 DOM,减去一些 IE7 怪癖)。
我喜欢链接到一个相关的咆哮/概述,它提供了一些关于使用和滥用 DOM 就绪回调的体面示例和一些时间琐事,以及关于为什么出色的性能可能低于一个健全的依赖管理框架。后者的主题过于广泛,无法在一个答案中用尽,但是像requirejs 文档这样的东西应该可以让您大致了解该模式的工作原理。
也许要考虑的另一种模式是构建一个 SPA - 单页面应用程序,它利用对内容块的异步访问,而不是在完整页面之间进行“传统”导航。该模式具有被低估但相当显着的性能优势,因为不必在每个页面上解析和重新执行共享的 javascript,这也将解决您对第三方 js 性能的(有效)担忧。毕竟,一个好的缓存策略会在加载时间上创造奇迹,但糟糕的 javascript 代码或大量框架的执行开销仍然存在。
更新:想通了。考虑到您的特定场景(即无法控制标记本身,并且希望成为最后一个执行的脚本),您应该将异步脚本元素插入 DOM包装到 0mssetTimeout
回调中:
setTimeout(function(){
//the rest is how GA operates
var targetScript = document.createElement('script');
targetScript.type = 'text/javascript';
targetScript.async = true;
targetScript.src = 'target.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(targetScript, s);
}, 0);
由于环境的单线程特性,jssetTimeout
回调基本上是在线程不再忙的时候就加入队列进行0ms延迟执行(这里有更详尽的解释)。所以浏览器甚至不知道需要加载,更不用说执行目标脚本,直到所有“更高优先级”代码完成!而且由于在添加脚本标记时 DOM 是可操作的,因此您不必在目标脚本本身中显式检查它(这对于从缓存“立即”加载它时很方便)。