延迟加载 JavaScript 和内联 JavaScript
Posted
技术标签:
【中文标题】延迟加载 JavaScript 和内联 JavaScript【英文标题】:Lazy loading JavaScript and Inline JavaScript 【发布时间】:2012-02-15 07:57:34 【问题描述】:我注意到在我的网站(用于工作)的<head>
中有很多<link rel="stylesheet" type="text/css" href="" />
和<script type="text/javascript" src="">
标签。还有更多的 JavaScript/CSS 文件只为特定页面加载(我们使用的是 CodeIgniter,文件路径被传递到标题视图)。
我正在考虑使用条件/异步加载器(例如 yepnope.js、head.js 等),但我注意到这样做有一个小问题。
在我们看来,有内联 JavaScript,有些使用 $(function())
,有些使用 $(document).ready(function())
,有些只是包含不在 ready
块中的代码(使用 jQuery)。
如果不编辑每个视图文件以将其代码包装在一个函数中并在加载 JS 文件时调用它,有没有办法延迟内联代码直到 JavaScript 被异步加载?
【问题讨论】:
没有。但是您可以使用window.$ = waitUntilCodeLoaded
让您的工作更轻松。这样你只改变了一半的内联代码。事实上,内联代码是不好的,把它全部放在外部
@Raynos:我正在考虑将所有代码移动到他们自己的文件中,但我们有很多视图,这需要一段时间。我希望有一个我可以做的快速、hacky 的解决方法。
我知道我应该将所有脚本移动到它们自己的文件中,但是有没有一种快速、简单的解决方法可以让我在需要时运行内联 JavaScript到?
也感兴趣:hacks.mozilla.org/2017/09/…
【参考方案1】:
您实际上可以延迟加载内联 javascript: 1-将内联脚本中的类型参数更改为:text/delayscript
来自
<!– Inline Script –>
<script type="text/javascript" language="javaScript">
/* Code */
</script>
到
<!– Inline Script –>
<script type="text/delayscript">
/* Code */
</script>
为脚本标签提供自定义的 Mime 类型 text/delayscript 会强制浏览器忽略其内容(请注意,完全忽略它将默认为 text/javascript)。
2- 延迟加载所有内联脚本 一旦 head.js(或您可能正在使用的其他框架)确认它延迟加载了所有外部 JS,您就可以获取所有自定义脚本标签的内容并将它们注入页面:
<script>
head.ready(function()
var
_head = document.getElementsByTagName("head")[0],
_script = document.createElement('script'),
_scripts = document.getElementsByTagName("script"),
_txt = "text/delayscript",
_contents = []
;
for(var i=0,l=_scripts.length;i<l;i++)
var _type = _scripts[i].getAttribute("type");
if(_type && _type.toLowerCase() ==_txt)
_contents.push(_scripts[i].innerhtml)
_script.type = 'text/javascript';
_script.innerHTML = _contents.join(" ");
_head.appendChild(_script);
);
为了更加优雅,您实际上可以将内联脚本保留在 DOM 树中的原始层次结构中,而不是像我上面建议的那样将其所有内容塞入一个脚本中,方法是将标记的内联脚本标记替换为新的一种具有 MIME 类型的文本/javascript:
head.ready(function()
var
_scripts = document.getElementsByTagName("script"),
_doc = document,
_txt = "text/delayscript"
;
for(var i=0,l=_scripts.length;i<l;i++)
var _type = _scripts[i].getAttribute("type");
if(_type && _type.toLowerCase() ==_txt)
_scripts[i].parentNode.replaceChild((function(sB)
var _s = _doc.createElement('script');
_s.type = 'text/javascript';
_s.innerHTML = sB.innerHTML;
return _s;
)(_scripts[i]), _scripts[i]);
);
【讨论】:
嘿,这很聪明。因此,告诉浏览器忽略脚本标签,然后在加载外部脚本时将它们重新加载到页面中。我喜欢它。 只要您的内联脚本中没有 document.write,这将起作用。因此,除了我上面提到的这种方法之外,还可以使用 yepnope.js(我强烈建议)、head.js 或 require.js 之类的东西,您可以动态延迟加载任何旧版 javascript,从而获得巨大的感知加载时间。 我没有使用document.write
,这很好。问题是,我想延迟加载外部,但内联代码依赖于它。使用这种方法,我可以在正确的时间运行脚本。 :-)
老问题(很好的答案)我知道,但这可能有用。如果 jQuery 也被延迟加载,并且您收到 $ 未定义的计时错误。将上述答案包装在 setInterval (var _si = setInterval...) 中,并且仅在加载 jQuery 时执行 for 循环。 (if (typeof $ != "undefined") window.clearInterval(_si); for(var i = 0, l = _scrip...)【参考方案2】:
您必须考虑将内联代码移到“外部”并包含在其中
<script defer="defer" type="text/javascript" src="">
【讨论】:
defer="defer"
到底是做什么的?
@Rocket: 只是提供参考,你必须要求回答者澄清:)
@Rocket "所以,它使文件异步" - 不是 【参考方案3】:
HTML5 为定义了src
的脚本引入了一个新的async
参数。
您可以直接将其添加到任何<script>
元素上:
<script src='/js/script.js' async></script>
但是:请记住,它不适用于内联脚本!
如果您的某些页面混合了外部脚本和内联脚本,如果您异步加载外部脚本,则意味着内联脚本实际上将在异步脚本之前执行......这可以有不良影响。
设置此布尔属性以指示浏览器应该(如果可能)异步执行脚本。它对内联脚本(即没有 src 属性的脚本)没有影响。
例如,如果您有以下配置:
<script src='/js/jquery.min.js' async></script>
<script>
// --> jQuery won't be loaded when this script will be executed!
// This will throw an error.
$(function ()
$('#element').doSomething();
);
</script>
【讨论】:
【参考方案4】:首先,我建议您非常仔细地分析客户端脚本的加载,不仅是第一次加载 JavaScript,还包括第二次加载相同的 JavaScript 文件或加载另一个文件页。如果 Web 服务器在脚本上正确设置了 ETag,或者如果您对 JavaScript 文件使用 HTTP 的其他缓存选项(有关详细信息,请参阅caching tutorial),则不会加载文件本身,只会重新验证缓存将会完成。因此,您描述的问题可能并不像看起来那么重要。
如果您决定动态加载某些脚本,您可以使用jQuery.getScript 并将所有相关代码放在success
回调中。如果您需要加载一个或两个 JavaScript 文件,这种方式会非常好用,但如果您需要加载具有更复杂依赖关系的 JavaScript 文件列表,则实现可能就不那么容易了。在这种情况下,您可以在<head>
中使用document.writeln
。使用<head>
内部的方法几乎没有缺点(详见here)。
【讨论】:
我知道如何动态加载 JavaScript。我的问题是我正在动态加载一个在身体某处使用的库。所以内联脚本失败,因为库不存在。我想知道是否有一种快速的方法来解决这个问题,而无需将所有 JavaScript 复制到他们自己的文件中。 @Rocket:为什么不能将使用库的代码(内联脚本)放在加载库的$.getScript
调用的success
句柄中?
可以,但是内联脚本标签很多。这样做需要很长时间。我想知道是否可以让浏览器自动为我执行此操作。
@Rocket:你可以读到here,如果你执行document.writeln("<script type='text/javascript' src='library.js'></script>"); document.writeln("<script type='text/javascript' src='scriptUsedLibrary.js'></script>");
,那么'library.js'和'scriptUsedLibrary.js'将被加载并行,但是以正确的顺序执行。因此,如果您在母版页中检测到您需要该库,您可以动态加载它。以<head>
中的方式下载的脚本在<body>
之前执行
@Rocket:我不能给你没有确切的参考,所以你应该测试一下。以我的经验,它总是完美的。您可以在脚本中包含您加载警报或其他一些长时间操作的脚本以更加确定。您可以检查加载时间,例如在 IE 开发工具的“网络”选项卡、Google Chrome 开发工具、Firefox 等中。该工具显示document.writeln
中包含的脚本将被并行加载到其他脚本,但如果下一个脚本需要前一个脚本,则没有任何问题,因为执行顺序正确。【参考方案5】:
根据您的设置,您可能希望从 www.cloudflare.com 查看该服务。他们当前的 beta 功能称为 Rocket loader,正是这样做的,包括内联脚本。
这是一项免费服务。试试看;)
此外,您还可以获得一个免费的缓存代理 :)
【讨论】:
我实际上在我的个人网站上使用 CloudFlare :-) @Rocket 您的用例是否允许您尝试一下?因为他们有脚本服务器端的代理进程,所以它对开发人员是透明的。包括内联脚本。火箭装载机确实有它的怪癖。例如,当火箭装载机打开时,CMS 混凝土 5 的(内联)编辑模式被破坏。 这是为了工作。我可以向老板提及,因为我无法控制服务器/DNS。 我同意@Michael Sørensen 关于火箭装载机不工作的观点。据我所知,从个人使用它开始,它只是使用 document.write 加载您的脚本,并且该方法非常错误。我们在我们的网站上使用 cloudflare,它破坏了网站。我们无法弄清楚为什么经过几个小时的愤怒并认为整个站点存在根本缺陷,“哦,这只是 cloudflare 中的错误设置”。非常烦人。【参考方案6】:创建内联函数,而不是内联脚本。然后在您的 javascript 文件的末尾,如果该函数存在,则调用该函数。
function inline_script() .. code ..
在您连接的 jquery+etc 异步 javascript 文件中:
if (typeof(inline_script) == 'function') inline_script()
您必须进行一些改组以确保每页只有一个 inline_script,或者如果您多次调用它,则使用某种缓存将它们放在一起。
尽管这个问题很老,但在当前时代这仍然是一个有趣的问题,异步脚本 = 非常棒
【讨论】:
【参考方案7】:看看使用headjs。这是一个不错的轻量级库,可以为您完成这一切。
【讨论】:
我看过 headjs,但问题是所有的内联 JavaScript。我想找出一种方法来延迟正文中脚本标签的执行,直到加载库。 您可以将脚本标签的内容包装在 cmets 中,然后正则表达式匹配 cmets 内的代码,然后在全局范围内对其进行评估 :)以上是关于延迟加载 JavaScript 和内联 JavaScript的主要内容,如果未能解决你的问题,请参考以下文章