缓存以HTML格式内联的Javascript

时间:2014-12-26 22:57:38

标签: javascript html

我们可以直接在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>

即,这样做:

  • 在第一次调用中,发送整个脚本并以某种方式告诉浏览器脚本哈希是abc
  • 稍后,当浏览器加载包含相同脚本的页面或其他页面时,它会将此密钥作为cookie发送。如果已收到密钥,服务器将仅呈现脚本的内容。

也就是说,如果浏览器已经知道了脚本,那么服务器将只渲染:

内联键控版本,后续提取(相同或其他页面)

<html>
<body>
  <script type="text/javascript" hash="abc">
  </script>
</body>
</html>

特别是脚本内容为空。

这样可以通过自然回退来缩短脚本提取时间。

以上可能吗?如果没有,是否可以采用其他替代方案?

3 个答案:

答案 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>

这掩盖了上半场。第二阶段是你的库看到一个空脚本。你的库应该做的另一件事是当它看到一个空脚本时,它应该在本地存储中查找具有该哈希的脚本,获取脚本的源并像你刚才那样执行它。

The Catch

现在总是在所有事情上进行权衡,我将突出我能想到的内容:

赞成

  • 您只需要一个所有内容的请求。初始页面加载包含脚本,后续页面由于缺少代码而变得更轻,这些代码已经被缓存。

  • 即时缓存清除。假设散列和代码是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

编辑:我误解了你的问题,删除了无关的部分。