我们在快速启动响应式Web设计中讨论的一种技术称为选择性内容加载(SCL)。 如果您希望以结构化的方式将少量数据加载到已加载的页面中,则该技术非常有用。 什么时候有用?
- 当您的服务器与最终用户的设备之间存在严重的带宽问题时(例如,在移动连接上,该移动连接在不良网络上移动并出现很多错误,并且必须处理小区切换)。
- 当您的页面在结构上与页面之间基本上相同时,只需重新加载内容即可保存许多请求。
- 如果您对内容进行了很好的分块,并且只想按顺序加载下一部分(例如,在Twitter feed上无限滚动)
这些问题中的一些可以通过良好的缓存和使用本地存储来解决,并且绝对应该将它们作为良好实践加以探讨。 但是,即使使用智能资产缓存,您仍然需要服务器往返来检索文档,所有页面HTML仍必须发送到浏览器,然后浏览器仍然必须呈现页面。
如果您的页面仅添加了几个额外的数据位(例如,一条推文)或仅更改了少量内容(例如,产品的详细信息),那么SCL对您来说可能是一个不错的选择。 这并非在每种情况下都有效,并且还会导致URL,浏览器历史记录以及搜索引擎将哪些内容爬到“页面”上的许多可能的问题(如果这对您很重要)。
从Jump Start RWD回顾我们的方法,这就是我们将在概念上做的事情:
- 在第一个页面请求中,我们将正常加载所有页面内容,包括网站导航,内容布局,CSS和JS文件。
- 页面加载后,我们将覆盖页面中的所有链接以使它们成为XHR,而不是标准的文档请求。
- 然后,我们将处理响应(XHR响应将仅是JSON中的内部页面内容,而不是整个页面),并覆盖页面中的内容。
- 然后,我们可以使用
pushState()
修改我们的浏览器历史记录(这样URL就会更新为可共享的内容,如果需要,我们可以向后退)。
这是一个应该简单说明这一点的示例。 为了保持简明扼要,内容已被有意截断。
我们将建立一个页面,该页面可以加载有关书籍的内容,而不必重新加载整个页面,而只是重新加载相关内容。 我们还将使用History API中的pushState()
来确保如果用户想要共享或返回URL,他们将能够做到这一点。
为使表达更简单,我们将使用jQuery进行DOM操作和一个名为Handlebars.js的JavaScript模板库。 如果您尚未检查过JavaScript模板选项及其功能,那么Handlebars是弄湿您的绝佳选择。
我们解决方案的核心依赖于以下事实:URL可以根据XHR还是普通HTTP请求做出不同的响应。 如果服务器收到正常请求,则视图将传递完整的HTTP响应(包含所有文档,然后是JS,CSS等)。 如果服务器收到XHR,它将以JSON响应,该JSON仅包含有关所请求书籍的数据。
因此,例如,“ Frankenstein”页面的标准HTTP响应如下所示:
<!DOCTYPE html> <html> <head> <script type="text/javascript" src=" https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js "></script> var original = null; var backtostart = true; <script type="text/javascript"> ($(document).ready(function() { var source = $("#story-template").html(); var template = Handlebars.compile(source); var story_link_handler = (function(evt) { evt.preventDefault(); $.get(this.href, function(data) { $("#contentarea").html(""); $("#contentarea").html(template(data)); history.pushState({url:data.location}, data.title, data.location); }, "json"); }); $("ul#storylist li a").bind("click", story_link_handler); $(window).bind("popstate", function(evt) { if (event.state) { url = event.state.url; $.get(url, function(data) { $("#contentarea").html(""); $("#contentarea").html(template(data)); }, "json"); backtostart = false; } else { if (! backtostart) { backtostart = true; $("#contentarea").html(""); $("#contentarea").html(original); } else { original = $("#contentarea").html(); backtostart = false; } } }); })); </script> </head> <body> <ul id="storylist"> <li><a href="mobydick">Moby Dick</a></li> <li><a href="gulliverstravels">Gulliver's Travels</a></li> <li><a href="frankenstein">Frankenstein</a></li> </ul> <div id="contentarea"> <article id="story"> <h1>Frankenstein</h1> <h2>Mary Shelley</h2> <p>Doctor creates artificial life</p> </article> </div> <script type="text/javascript" src="handlebars.js"></script> <script id="story-template" type="text/x-handlebars-template"> <article> <h1>{{title}}</h1> <h2>{{author}}</h2> <p>{{synopsis}}</p> </article> </script> </body> </html>
注意,您可以在本文末尾链接的zip文件中下载本文中使用的代码
但是,XHR的等效JSON响应将改为如下所示:
{ "location": "/frankenstein", "title": "Frankenstein", "author": "Mary Shelley", "synopsis": "Mad doctor creates artificial life" }
请求选择性加载工作所需的所有代码,并在第一个请求中加载。 之后,我们只获取数据,然后将其加载到模板中。 让我们看一下代码是如何工作的。
<script id="story-template" type="text/x-handlebars-template"> <article> <h1>{{title}}</h1> <h2>{{author}}</h2> <p>{{synopsis}}</p> </article> </script>
注意,您可以在本文末尾链接的zip文件中下载本文中使用的代码
车把使用script元素为文章的外观创建模板(该内容不会由浏览器呈现,因为它不会对其类型进行任何操作)。 变量位置是使用{{variable}}
语法定义的。 您可以使用在此实例中未使用的Handlebar(条件,循环,块执行等)做更多的事情。 注意脚本的ID,我们需要这个ID,以便将其传递到Handlebars模板编译器中。
在准备文档的功能中,我们从上面定义的模板脚本标签中获取HTML,然后将其编译为模板对象,以便以后与新数据一起使用。
var source = $("#story-template").html(); var template = Handlebars.compile(source);
接下来,我们定义一个可用于链接onclick
事件处理程序的函数。 在这里,我们只是通过阻止文件的默认行为来停止对文件的实际请求。 在此,我们制作了一个jQuery XHR,该JSON将JSON返回到在链接的HREF
属性中定义的URL。
var story_link_handler = (function(evt) { evt.preventDefault(); $.get(this.href, function(data) { $("#contentarea").html(""); $("#contentarea").html(template(data)); history.pushState({url:data.location}, data.title, data.location); }, "json"); });
当响应返回时,我们只需覆盖保存所有图书数据的div
内容区域。
$("#contentarea").html(template(data));
我们还使用pushState()
将刚刚请求的URL推送到浏览器历史记录中,以便可以使用“后退”按钮向后移动。
history.pushState({url:data.location}, data.title, data.location);
但是,为了使它对用户“正常工作”,使用pushState()
并不是全部。 接下来,我们在窗口上创建一个popstate
事件处理程序,以便当用户单击后退按钮时,我们可以使用该书的适当数据来更新内容。 在这种情况下,我们将使用XHR重新获取数据。 使用pushstate,可以将数据存储在state
对象中。 在我们的例子中,数据量很小,并且用其他数据(尤其是在移动设备上)加载用户的浏览器是个坏习惯,因此只有在可以保证数据量很小的情况下,才这样做。
$(window).bind("popstate", function(evt) { if (event.state) { url = event.state.url; $.get(url, function(data) { $("#contentarea").html(""); $("#contentarea").html(template(data)); }, "json"); } });
我们需要考虑使用此技术的一件事是当浏览器返回列表的开头时发生的情况。 也就是说,您已经将所有XHR从堆栈中弹出,然后又回到了开始的位置。
为了解决这个问题,我们使用一个标志来确定是否回到起点,并保存#contentarea
的内容,以便我们替换它。 您可以使用其他技术,例如简单地隐藏原始内容区域或存储原始文档的JSON。
然后,我们更新popstate
事件以检查是否没有event.state
。 如果是这样,我们将恢复为原始形式。
$(window).bind("popstate", function(evt) { if (event.state) { url = event.state.url; $.get(url, function(data) { $("#contentarea").html(""); $("#contentarea").html(template(data)); }, "json"); backtostart = false; } else { if (! backtostart) { // revert the content to the original backtostart = true; $("#contentarea").html(""); $("#contentarea").html(original); } else { // store original content to retrieve later original = $("#contentarea").html(); backtostart = false; } } });
最后,我们将click
事件处理程序添加到所有相关链接。 在我们的实例中,我们仅使用列表中的链接,但实际上,您可以根据class
或HREF
属性对整个链接范围进行此操作。
$("ul#storylist li a").bind("click", story_link_handler);
页面的其余部分是页面结构和所请求的实际内容,在本例中是/frankenstein
URL。
可以看出,这种方法为我们提供了一个不错的响应式设置。 最初的页面请求要重一些(在本例中约为1Kb),但提供了布局页面和提供交互所需的所有支架。 后续请求的好处是仅需返回非常小的数据片段,然后将这些数据片段加载到模板中。
使用pushState()
更新URL,这意味着用户仍然可以使用意图共享URL或复制并粘贴,并且该URL对于共享它的任何人都将正常工作。 您还可以获得每个文档仍然正确存在的好处–这意味着搜索引擎仍然可以根据需要正确索引您的网站。
使用此技术时需要注意的一件事是,如果我们拥有许多不同模板中存在的内容,则必须通过加载所有不同的内容模板来破坏仅通过XHR加载数据片段的好处。进入首页请求并在用户使用之前对其屏蔽。 别忘了,无论是否使用它,仍然必须加载所有HTML。
这种技术特别有效的地方是在“无限滚动”情况下,例如内容流或很长的文章。 内容类型几乎没有改变(或仅以一种非常明确的方式改变)—整体上,此技术对“页面内容”起作用,而惰性加载技术对页面内的图像起作用。 如果您花了一些时间对内容进行分块,这会特别有效,因为这意味着即使搜索引擎会很高兴地跟踪用户,也可以避免用户点击“转到第2页”。
在安德鲁(Andrew)的新书《克雷格·沙尔基(Craig Sharkie):快速启动响应式Web设计》中深入研究响应式Web设计领域 。