我们可以直接在HTML中内联Javascript,而不是外部.js文件,即
外化版
<html>
<body>
<script type="text/javascript" src="/app.js"></script>
</body>
</html>
内联版
<html>
<body>
<script type="text/javascript">
// app.js inlined
</script>
</body>
</html>
但是,不推荐:
主要原因是缓存和预编译 - 在外化版本中,浏览器可以为多个页面下载,预编译和存储文件一次,而对于内联版本则不能这样做。
但是,是否有可能沿着这些方向做点什么:
内联键控版
<html>
<body>
<script type="text/javascript" hash="abc">
// app.js inlined
</script>
</body>
</html>
即,这样做:
也就是说,如果浏览器已经知道了脚本,那么服务器将只渲染:
内联键控版本,后续提取(相同或其他页面)
<html>
<body>
<script type="text/javascript" hash="abc">
</script>
</body>
</html>
特别是脚本内容为空。
这样可以通过自然回退来缩短脚本提取时间。
以上可能吗?如果没有,是否可以采用其他替代方案?
答案 0 :(得分:4)
我不知道如何做你所要求的事情,所以我会提供一个可能仍然适合你需要的替代方案。
如果你真的在低延迟首页加载之后,你可以内联脚本,然后在页面加载后,通过url加载脚本,以便它可以在浏览器缓存中用于将来的请求。在您通过直接网址加载脚本后设置Cookie,以便您的服务器可以确定是否内联脚本或提供外部脚本网址。
第一页加载
<script>
// inlined my-script.js goes here.
</script>
<script>
$(function(){
// load it again, so it's in the browser cache.
// notice I'm not executing the script, just loading it.
$.ajax("my-script.js").then(function(){
// set a cookie marking this script as cached
});
});
</script>
第二页加载
<script src="my-script.js"></script>
显然,这有一个缺点,就是它会加载脚本两次。当您使用新代码更新脚本时,它还会增加额外的复杂性 - 您需要确保将cookie用于旧版本。
除非你真的觉得需要优化第一页,否则我不会为此烦恼。在您的情况下可能是值得的。
答案 1 :(得分:2)
这是一种有趣的方法(在被通知窃听之后:P)
您可以让服务器以这种方式呈现您的脚本。请注意奇怪的type
属性。那是为了防止脚本执行。我们将在一秒钟内完成。
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
//inline script
</script>
然后创建一个库,查找具有奇怪类型的这些脚本,获取这些脚本的innerHTML
,并在全局上下文中执行它们,就像它们正常执行一样(通过eval
或{{ 1}})。这使它们像普通脚本一样执行。 Here's a demo:
new Function
...然而
由于你有脚本源(innerHTML),你可以在本地缓存它们(比如localStorage)并使用哈希作为其标识符。然后你可以在cookie中存储相同的哈希值,未来的页面请求可以告诉服务器“嘿,我已经用[哈希]缓存了脚本。不要再在页面上打印脚本了”。那么你将在未来的请求中得到这个:
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
alert(a);
</script>
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
alert(b);
</script>
<script>
// Let's say we have a global
var a = "foo";
var b = "bar"
// Getting the source
var scripts = Array.prototype.slice.call(
document.querySelectorAll('script[type="text/cacheable"]')
);
scripts.forEach(function(script){
// Grabbing
var source = script.innerHTML;
// Create a function (mind security on this one)
var fn = new Function(source);
// Execute in the global scope
fn.call(window);
});
</script>
这掩盖了上半场。第二阶段是你的库看到一个空脚本。你的库应该做的另一件事是当它看到一个空脚本时,它应该在本地存储中查找具有该哈希的脚本,获取脚本的源并像你刚才那样执行它。
现在总是在所有事情上进行权衡,我将突出我能想到的内容:
您只需要一个所有内容的请求。初始页面加载包含脚本,后续页面由于缺少代码而变得更轻,这些代码已经被缓存。
即时缓存清除。假设散列和代码是1:1,那么更改内容应该更改散列。
这假定页面是动态的并且从不缓存。那是因为如果您碰巧使用新哈希创建了一个新脚本,但客户端缓存了该页面,那么它仍然会使用旧哈希这样的旧脚本。
由于内联脚本,初始页面加载会很重。但是这可以通过使用服务器上的minifier压缩源来克服。通过在服务器上缓存缩小的结果,也可以克服缩小的开销。
安全。您将使用<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x"></script>
或eval
。当未经授权的代码设法潜入时,这会带来很大的威胁。此外,由于缓存,威胁仍然存在。
不同步的网页。如果你得到一个空的脚本会发生什么,它的哈希值不在缓存中?也许用户删除了本地存储?您必须向服务器发出请求。既然您需要源代码,那么您必须拥有AJAX。
脚本不是“正常”。您的脚本最好放在页面的末尾,这样就可以解析所有内联脚本。这意味着您的脚本执行起来很晚,而且从来没有被浏览器解析过。
存储限制。 localStorage的大小限制为5-10MB,具体取决于我们正在谈论的浏览器。 Cookie一般限制在4KB。
请求大小。请注意,cookie会根据请求发送到服务器,并在响应时发送到浏览器。额外的负荷可能比它的好处更麻烦。
添加了服务器端逻辑。因为您需要知道需要添加什么,所以您需要对服务器进行编程以执行此操作。这使得客户端实现依赖于服务器。切换服务器(例如从PHP到Python)并不容易,因为您需要移植实现。
答案 2 :(得分:1)
<击>
如果<script>
未作为type=text/javascript
引入,则不会执行。
所以你可以有许多像这样的标签:
<script type="text/hashedjavascript" hash="abc">...</script>
<script type="text/hashedjavascript" hash="efg">...</script>
然后在加载DOM时,选择一个并对其进行评估。
我在这里举了一个例子:http://codepen.io/anon/pen/RNGQEM
击>
但它闻起来很糟糕。获取两个不同的文件肯定更好。
实际上你应该做的是,有一个文件my-scripts.js
包含你的每个脚本的代码,包含在一个函数中
// file: my-scripts.js
function script_abc(){
// what script abc is supposed to do
}
function script_efg(){
// what script efg is supposed to do
}
然后执行你告诉你的任何内容。这就是AMD构建器将多个文件连接在一起的方式。
还要查找一个AMD库,例如requirejs
编辑:我误解了你的问题,删除了无关的部分。