八、让用户旅程响应迅速
用户的旅程是通过网站的一系列步骤,因此它是使用体验的一个重要因素。用户的旅程可能因网站而异,这取决于谁是目标受众,他们需要从网站获得什么,以及他们将如何使用网站。
有两个主要因素促成了一个网站的用户之旅:企业的需求和用户的需求。当涉及到业务需求时,这是站点的业务目标被确定的地方;这些可能是为了推动销售,增加参与度,或发展品牌知名度。当谈到用户的需求时,这就是用户在访问网站时试图达到的目标,包括他们的目标和动机。
考虑到这一点,让我们来看一个以健康俱乐部网站为特色的用户旅程示例。在这种情况下,用户需要找到有关健身俱乐部的信息,以便确定他们是否有兴趣注册。对于企业来说,他们的需求是销售健身俱乐部会员资格。因此,用户之旅应该通过研究如何将它们结合起来来满足这两种需求。这是通过清晰的用户旅程来实现的,首先为用户提供他们需要的关于健身俱乐部的信息,但最终通过将他们带到注册页面来鼓励他们注册。
虽然这里的例子并不复杂,但并不是所有用户的旅程都这么简单,或者在所有设备上都适用。如果旅程在您支持的一个或多个设备上没有意义,您应该考虑如何调整旅程,为用户提供更好的体验。
这一章将教你如何调整用户通过你的网站的旅程,以优化他们的用户体验,不管他们使用的是什么设备。为此,我将介绍:
Adapting your content Adapting the user’s journey User testing your responsive site Using web analytics tools
调整您的内容
如果您正在构建一个网站,组成网站的 HTML、CSS 和 JavaScript 允许您将内容呈现给用户。
如果您在浏览器中单独打开一个 HTML 页面,而没有应用任何 CSS 或 JavaScript,您只会看到应用了浏览器默认样式的站点,如果结构正确,它应该仍然有一个有意义的流。
如果你把 HTML 看作是网站的骨架,那么 CSS 就是网站的血肉,是使网站更加完整的衬垫和细节。通过添加 CSS,您已经开始添加视觉效果,帮助内容更容易阅读并提供结构。根据这个类比,JavaScript 就像是网站的神经系统,协调已经添加到网站中的功能。
正如这个类比所强调的,内容是网站的核心,因此,当构建一个响应式网站时,你要确保它以最好的方式呈现。实现这一点的方法是根据观看内容的设备来调整内容。
当选择修改响应式网站的内容时,你需要考虑各种不同的事情。我们现在来看看在改编我们的内容时需要考虑的关键因素。
视口的大小
由于用于查看站点的设备有如此多种不同的形状和大小,这可能会对您选择如何调整内容产生重大影响,因为您需要考虑用户在各种不同视区大小上的体验。
当响应式设计开始流行时,基于视口大小调整内容的两种方法开始流行,第一种是隐藏被认为不太重要的内容,第二种是将内容堆叠在彼此之上。
让我们从第一种方法开始,你将很快看到使用它可能产生的问题。在他的书《响应式网页设计》中,Ethan Marcotte 谈到了将不同体验的内容分割开来是不可持续的。尽管他特别谈到了移动和桌面网站的分离,但是当你采取简单的隐藏内容的方法来使一个响应性网站很好的在移动上运行时,你就把用户看到的内容分割了。这里的关键区别在于,用户不能选择简单地切换到桌面站点来查看全部内容,这使得这些内容在他们使用的设备上完全不可访问。
虽然简单地隐藏一些内容可能会使页面的长度更令人满意,但问题是,如果对用户完全隐藏,您可能会面临这样的风险,即它可能实际上是用户正在寻找的内容,并可能在 Google 上搜索时将他们带到该页面。真正需要考虑的是内容是否不重要到需要在移动设备上显示,以及它是否对用户在更大的视窗上有任何真正的好处。如果它不能给用户带来真正的好处,那么最好把它从网站上彻底删除,而不是藏在手机上。
第二种方法是在较大的设备上并排堆叠内容。这样做的问题是,如果有问题的网站有大量的内容,当堆叠起来时,这可能会导致一个很长的页面,而这又会导致用户正在寻找的内容在页面的很下方。
尽管这两种方法都非常流行,尤其是在从桌面优先的角度构建的网站上,正如我前面提到的,它们都有缺点。为了克服这些方法的缺点,您可以选择将这些方法结合起来。这里的解决方案是修改响应式设计上的内容,使你觉得不太重要的内容可以折叠,这意味着用户可以选择扩展部分,如果他们有兴趣阅读它,而不必求助于一个很长的页面。
某些类型的内容比其他内容更适合改编,一系列常见问题就是一个例子。您可以在不同的视窗尺寸上以不同的方式显示这些内容,而不会对用户完全隐藏内容。在较小的设备上,您可以折叠 FAQ,以便只看到问题,直到单击问题以展开答案。这有利于用户,因为他们可以快速找到他们需要答案的问题。在较大的设备上,您可以默认打开它,因为空间和长度不是问题。
设备支持的功能
一些内容特定于支持给定功能的设备。您需要考虑您的目标设备,以及它们是否具有您可以整合到您的网站中的功能,但是记住同样大小的其他设备可能没有这些功能也很重要。当涉及到只有部分使用网站的设备支持的目标功能时,您需要尽可能确保那些使用不支持某个功能的设备的人不会得到更糟糕的体验。
特定于设备功能的内容的一个例子是“查找我最近的商店”,其中您将使用地理定位 API 来确定用户的位置,然后查找离他们最近的商店。在这种情况下,并非所有设备都支持地理位置 API,因此默认情况下,您可以显示一个输入字段,用户可以使用它来输入他们的位置。对于支持地理定位 API 的设备,您可以通过检测 API(使用第九章中讨论的功能检测技术)来逐步增强网站,然后添加使用 API 检测用户位置所需的功能。
用户输入方法
用户与 web 内容交互的方式会对您显示内容的方式产生重大影响。在考虑如何调整内容时,您需要考虑各种各样的输入方法,其中一些最常见的是键盘、鼠标和触摸屏。
与基于功能定位内容不同,这里您无法可靠地检测用户正在使用的输入法。你所能做的最好的事情就是以一种在你所支持的不同输入法中都能很好工作的方式来显示内容。然而,您可以提供更好的体验的地方是通过基于用户用来与页面交互的输入方法添加不同的交互。
根据所用的输入方法,可以使用不同交互的一个例子是转盘。在用户使用键盘浏览转盘面板的情况下,他们将能够使用左右键遍历内容。当使用鼠标时,他们可以使用按钮来向前和向后导航。对于触摸输入,您可以选择允许用户在不同的面板之间使用滑动手势。
第二个例子是用户使用能够在内容面板之间滑动的触摸设备进行导航,类似地,用户使用鼠标进行导航,当鼠标悬停在元素上时可以看到不同的内容。这只是对某些输入法有意义的用户交互的两个例子。因为这些输入法直接控制内容的显示方式,所以您需要考虑如何适应不同的响应状态。
看了这两个例子,你会发现不同的交互方式会给用户带来非常不同的体验,因此你需要了解利用它们的不同方式来提供最佳体验。
尽管您无法可靠地检测用户使用的是哪种输入法,但是您可以添加对不同类型事件的支持,比如触摸事件和键盘事件(比如 keydown 和 keyup)。
重要的是要记住,在构建网站时,有可能会开发新的输入法,如果新的输入法变得流行,您可能需要调整您的站点来支持它。
内容本身
当构建一个响应式网站时,你的内容会陷入两种情况之一:你将从现有的需要修改的内容开始,以便做出响应;或者你将足够幸运,能够从你的内容开始。
改编现有内容
当开始一个新的响应式构建或修改现有站点以响应时,很可能您已经有了现有的内容可以使用,可能来自站点的旧版本。因此,在考虑对现有内容进行改编时,考虑这些内容非常重要。
在处理现有内容时,首先需要考虑的事情之一是它给用户增加了什么价值。如果你觉得有些内容对你的用户没有真正的帮助,你可以和你的利益相关者讨论这个问题,并在他们的许可下删除它。
为响应式网站构建内容
如果您正在为响应式站点构建新内容,那么您将处于优势地位,因为您可以首先专门为较小的设备构建内容,然后再调整内容以在较大的视窗中更好地工作,而不是调整内容以在较小的设备上工作。这意味着,你不要试图从内容中抽离,而是要看看通过调整内容来适应它,你能为更大的体验增加什么价值。
然而,在为这些更大的设备调整网站内容时,你不应该简单地试图将额外的内容放入页面。你应该首先把你在小设备上使用的内容隔开,然后看看如何使用增加的间距和图像来增强体验。
区分内容优先级
在决定是修改现有内容还是为响应站点创建新内容后,考虑如何对内容进行优先级排序是很重要的。您还需要考虑如何确保每个设备的最相关内容以可见的方式显示。让我们以一家餐馆为例。当用户使用台式计算机时,他们可能想要找到关于餐馆的信息,可能浏览菜单,看看餐馆有什么样的气氛,并查看关于餐馆所在区域的信息,如当地剧院和其他要做的事情。在移动设备上,用户的目标可能大不相同;他们可能只想知道餐馆的营业时间,或者预订并找到从他们当前位置到餐馆的方向。这些用户目标的差异会对你如何调整网站内容产生深远的影响。
要以这种方式区分内容的优先级,您可以使用 CSS Flexbox 规范的order
属性。首先,让我们用一个简单的包装器div
将一些 HTML 放在一起,这个包装器包含三个块,这三个块将在不同的视窗尺寸下以不同的顺序显示。由于 flexbox 没有可用的 polyfill,为了在不支持 flex box 的浏览器上正确排序 flex 项目,我们应该按照我们希望它们在不支持的浏览器中显示的顺序来排序子元素。这方面的代码应该是:
<div class="wrapper">
<div class="block block1">
<p>This is the first block</p>
</div>
<div class="block block2">
<p>This is the second block</p>
</div>
<div class="block block3">
<p>This is the third block</p>
</div>
</div>
将 HTML 放在一起后,下一步是告诉浏览器将包装器显示为 flex 容器,您可以通过将display
属性的值设置为flex
来实现这一点。这为我们的 flex 容器的子元素启用了 flex 上下文,这些子元素被称为 flex 项目。
在设置我们的 flex 容器时,我们还可以指定它包含的元素的方向。这可以通过为flex-direction
属性设置一个值来实现。flex-direction
属性的默认值是row
,它从左到右并排显示 flex 项目。我们可以使用的可选值有:column
,它将 flex 项目堆叠在另一个项目的顶部;row-reverse
,它将颠倒项目的顺序;以及column-reverse
,它将把项目以相反的顺序堆叠在另一个项目的顶部。
在这个例子中,让我们对堆叠在一起的元素进行排序。为了实现这一点,我们需要将 flex 样式应用于我们的包装器,第一步是将display
属性设置为值flex
,然后将属性flex-direction
设置为值column
。我们应用于包装器的 CSS 如下所示:
.wrapper {
display: flex;
flex-direction: column;
}
已经告诉浏览器包装器的子元素是 flex 项目,现在可以设置它们的显示顺序。之前我们选择了 flex 项目的顺序,因此默认情况下,项目会按照我们希望的方式显示,我们的内容在不支持flexbox
的浏览器中会被排序。虽然这意味着在这种情况下,我们的 HTML 不是移动优先的,但这确实意味着对于支持flexbox
的浏览器,我们正在逐步增强网站。这意味着如果我们想要在较小的设备上有不同的顺序,我们将需要定义我们想要项目出现的顺序。我们通过用它应该出现的位置的值给order
属性设置一个值来做到这一点。
.block1 {
order: 2;
}
.block2 {
order: 1;
}
.block3 {
order: 3;
}
对于较大的设备,您可能希望再次更改块的顺序,因此您可以简单地使用媒体查询,为每个块更改order
的值,如这段代码所示:
@media only screen and (min-width: 1200px){
.block1 {
order: 3;
}
.block2 {
order: 2;
}
.block3 {
order: 1;
}
}
新的 Flexbox 规范提供的灵活性在构建站点时非常有用,虽然我在这里讨论的是如何使用它通过基于视窗大小设置顺序来区分不同内容的优先级,但规范本身要宽泛得多,提供了新的、高效的方法来布局站点。
目前使用 Flexbox 规范有一些限制。首先,它并不被你可能必须支持的所有浏览器所支持,支持 Flexbox 的最早版本是 Internet Explorer 10。
也有两个独立的规范,旧规范在一些第一批实现 Flexbox 的浏览器中,新规范在较新的浏览器中实现。例如,Internet Explorer 10 使用旧规范,但 Internet Explorer 11 使用新规范。这意味着在编写代码时,您需要考虑同时支持这两种规范,以支持最广泛的浏览器。
虽然它并不被全面支持,但是你不需要所有的浏览器都支持 Flexbox 来利用它来优先排序你的内容。您可以在 HTML 中默认一个通用的内容顺序,然后使用 Flexbox 以不同的视窗大小对内容进行重新排序,以适合支持它的浏览器。
最新浏览器支持的完整详细信息可在 http://caniuse.com/flexbox
的“我可以使用吗”网站上找到,其中指出了部分支持,很可能实现使用的是旧规范。
适应用户的旅程
重要的是,在你着手建立一个网站之前,你要确保你已经适当地计划了用户可以通过这个网站的旅程。作为其中的一部分,您需要考虑如何调整用户的旅程,以便在具有不同视窗大小、不同输入方法和不同功能支持的各种不同设备上很好地工作。
在响应式设计出现之前,很容易规划出用户在一个网站中可能经历的不同旅程,通常有一个起点和一个你想让他们到达的终点,并有有限的几种方式让他们在两者之间穿行。虽然在某些情况下,你可以用响应式设计来维持这种方法,但基于用户使用的设备来构建额外的用户旅程通常是有意义的。
常见站点交互
有各种各样的站点交互构成了用户旅程的一部分,当构建一个响应式站点时,检查每一个交互以确定它们是否能很好地响应是很重要的。这样做的时候,您可能需要考虑改进用户体验所需的任何调整。让我们看一些你可能在你的站点上实现的用户交互的例子,并探索你可能想要如何调整它们以在你的站点支持的各种设备上提供更好的体验。
模态窗口
第一个常见的站点交互是模式窗口。模式窗口是一种方法,在这种方法中,您可以在用户当前查看的页面内容之上的容器中显示新内容。最初是为了显示更大版本的图像,后来它的使用范围扩大了,并被用于许多不同目的的大量网站。由于这种在用户当前查看的页面顶部显示内容的灵活性,它允许您显示与当前页面相关的内容,而无需将用户导航到新页面。
如前所述,模态窗口最初且仍然常见的用途是显示图像缩略图的放大版本。在较大的视口中,这可以很好地工作,但是,通常在较小的设备上,“放大”的图像在灯箱中实际上看起来很小。对于不同长宽比的图像来说尤其如此:当设备是纵向的时,高的图像可能看起来不错,但对于宽而短的图像,图像会显得小。
模式窗口的另一个用途是通过允许用户在表单中输入数据来收集用户信息。之所以把它放在模态窗口而不是另一个页面中,是因为你想让用户更方便。这种数据收集可以只是一个联系表单,也可以是网站的登录表单。
对于这两个例子来说,模态窗口在较小的设备上并不是正确的答案,因为它为用户提供了不太好的体验。然而,在更大的设备上使用一个仍然是有意义的。这是一个很好的例子,说明您可以轻松地为使用较小设备而不是较大设备的用户提供略有不同的旅程,目的是为每个设备提供最佳体验。
模态窗口在较小的设备上提供较差的体验有几个主要原因。首先,用于定位模式窗口的技术(CSS 属性 position 设置为值 fixed)不能在所有移动浏览器上可靠地工作。事实上,布拉德·弗罗斯特 1 决定研究固定定位在移动浏览器中的实际效果。在他的结果中,他发现跨浏览器的支持是不一致的;即使是看似支持位置固定的移动浏览器,有时在实现时也会有些古怪。
第二个原因是,在较小的视口中,即使显示正确,对于移动用户来说,拥有模态窗口的整个体验可能会很笨拙。根据您在模式中显示的内容的长度,它可能会超出浏览器视窗的高度。在这种情况下,用户需要滚动模式窗口中的内容。不幸的是,在一些移动浏览器中,滚动条仅在你滚动内容时显示,所以用户可能不清楚他们是否可以滚动内容。除此之外,当用户想返回到页面的主要内容时,如何做可能并不明显。在更大的视口中,用户有多种方式可以退出模态;通常可以通过单击关闭按钮、在模式窗口外单击或按键盘上的 Esc 键来退出它们。在较小的视口中,模式窗口周围没有任何空间允许用户单击或触摸来退出视口,并且在触摸设备上,用户可能没有键盘。虽然您可以用与其他视窗大小类似的方式实现关闭按钮,但全屏模式窗口可能会让用户感觉他们在一个新页面上。在这种情况下,他们可能会使用浏览器导航按钮,这可能会将他们带到错误的页面。
对此的解决方案是,在较小的视口中,用户将在新页面上打开内容,但在较大的视口中,它将继续显示在模式窗口中。这样做的好处是,在较小的设备上,用户可以像查看任何其他页面一样查看内容,还可以访问导航和其他信息,而模态窗口会隐藏这些信息。
为了说明这可能是如何工作的,让我们看一个图片库的例子。在较大的视窗中,当用户点击图库图像时,它会在灯箱中显示较大的图像。这个旅程不会让用户离开画廊,他们可以简单地关闭灯箱,继续观看画廊的图像。我已经在图 8-1 中对此进行了说明。
图 8-1。
The journey the user takes on larger viewports
如果您了解这在较小的视窗中是如何工作的,那么您可以调整用户在站点中的行程,以便在点击图像时,它会打开一个新页面来显示较大的图像。此外,在新页面上,您需要确保包含返回到原始图库的链接。图 8-2 显示了用户浏览网站的步骤。
图 8-2。
The journey the user takes on smaller devices
实现这一点的方法是在图像周围设置一个链接到新页面的锚链接,对于较大的视窗,只需使用 JavaScript 在模式窗口中加载图像。我将在第九章中解释如何实现这一点。
产品搜索
通常情况下,如果你的网站是一个产品,你的用户很容易快速找到你的产品信息;然而,对于销售各种不同产品的大型零售网站来说,用户能够快速方便地找到他们正在寻找的产品和相关信息是非常重要的。
用户在大型零售网站上搜索产品的典型过程是在搜索框中输入产品名称,然后进入搜索结果页面,如图 8-3 所示。
图 8-3。
Path to the results page after entering a search term
这个旅程在一个响应式网站上工作得很好,但是,它没有考虑用户的情况,在这种情况下,结合了用户的当前位置和实际搜索的目的。
传统的情况下,用户可能会在家里的台式电脑上搜索产品,比较功能和价格。这种传统情况是过去大多数用户使用网站的方式。然而,随着智能手机的普及,出现了新的情况,有人可能在外面购物,看到他们感兴趣的产品,并希望确保他们得到最好的价格。为了做到这一点,用户拿出他们的手机,在网上搜索产品,也许是通过访问你的网站,搜索产品。
这种情境行为在以前是不会被考虑,但是对于一个响应式站点,开发人员现在需要开始假设一个站点可以在任何地方使用,因此在设计网站时需要考虑这一点。部分问题是访问者的情境行为不容易被检测到。开发人员不知道用户是在商店里还是在家里,虽然您可以尝试使用地理定位 API 来确定他们的位置,但这是不切实际的,原因有几个。您必须知道您的客户可能在的所有不同商店的位置,并且您还需要请求用户允许您使用他们当前的位置。
因此,与其试图检测用户的情况,不如对预期的用户情况进行有根据的猜测。要做到这一点,您应该研究用户选择使用特定设备的典型情况,然后考虑在这种特定情况下他们会从哪些功能中受益最多。此外,如果你已经有了一个网站的在线版本,并且你可以访问分析数据,你可以看看你的用户已经在使用你的网站做什么,以告知你如何针对某种情况优化你的网站。
如果您再次查看产品搜索的示例,您可能会认为用户在商店时很可能会使用该站点来查找价格。很自然,您会希望确保他们能够以尽可能少的步骤完成这项工作。这样做的第一步是优先考虑用于产品发现的内容;对于销售各种产品的网站,这可以通过确保当页面加载时,产品搜索框立即对用户可见来实现。如果你的网站专注于销售一些你自己的产品,你应该展示一些基本信息,包括价格,这样用户甚至不用离开他们登陆的页面就能找到他们想要的信息。
在较大的视窗中,虽然搜索仍然是网站的一大特色,但用户更可能只是浏览网站,并不完全确定他们在寻找什么(可能是为朋友寻找礼物,但不确定买什么)。因此,你要确保产品发现是网站的一个重要部分,根据以前的购买显示类别和建议。
除了如何确定内容的优先级,您还应该考虑如何使用渐进式增强来利用设备提供的新功能。产品搜索的一个例子是允许用户拍摄产品或条形码的照片来搜索产品。对用户的好处是,他们不需要输入复杂的产品名称,比如“Samsung ue40 f 5000 40”LED TV,而是简单地拍一张照片。
为了实现这一点,您需要确定设备是否支持摄像机 API,然后对于那些支持的设备,为用户提供一个额外的按钮,他们可以激活该按钮来加载摄像机。如果只找到一种产品,网站可以直接把用户带到该产品,否则他们将被带到一个搜索结果页面。本例的用户旅程如图 8-4 所示。
图 8-4。
User journey for devices that support the camera API
选项卡式容器
组织网站内容的一种方法是将内容分成用标签切换的容器。这些使您能够将内容分成有意义的部分,允许站点的用户通过在选项卡上显示内容的清晰标题来直接找到他们正在寻找的内容。它们在较大的显示器上工作得非常好,然而,在较小的设备上,标签会变得拥挤,通常没有足够的空间来正确显示标题。在这种情况下,有必要调整功能,使其更适用于较小的视口。
在较小的视口中更改功能的一种方法是对其进行调整,使选项卡面板在较小的视口中像手风琴一样显示。通过这样做,标题将有更多的空间可见,用户仍然可以点击不同的内容容器。
下拉菜单
对于 web 站点来说,将下拉菜单作为主导航的一部分是很常见的,通常用户将鼠标光标悬停在下拉菜单上就会激活这些下拉菜单。不幸的是,这一功能并不能很好地适用于用户使用触摸屏而不是鼠标的移动设备。一些移动浏览器试图通过在用户点击项目时触发 CSS 悬停来解决这个问题,然而,通常用户很难禁用他们悬停的内容。因此,您需要考虑如何调整这些下拉列表,以便更好地跨设备工作。Twitter Bootstrap 下拉菜单使用的一个建议是完全放弃悬停功能,只允许用户点击或点击来切换下拉菜单的打开或关闭。
视差
视差是一种当用户上下滚动时以不同速度滚动页面元素的技术。这种技术允许您在用户向下滚动时显示动画,以增加页面的深度。
该技术擅长的地方是在较大的视口中,那里有用于过度动画的空间。这是一种非常好的讲述故事的技巧,无论是关于产品、服务还是公司。当您开始考虑如何在更小的视口中工作时,问题就出现了,因为那里没有足够的空间来讲述故事。
开发人员在试图让 parallax 在小型设备上工作时面临的一个问题是,当用户滚动时,parallax 通常使用固定定位来定位元素。不幸的是,小型设备不能依靠固定定位,因为许多移动浏览器的实现不完整或有问题。
另一个问题与浏览器触发onscroll
事件的方式有关。在桌面浏览器上,当用户滚动时,它被连续触发,但是在较小的设备上,直到滚动动作停止时,才触发onscroll
事件。这意味着当用户滚动时,你不能使用onscroll
事件来激活元素,因为元素直到用户停止滚动后才会更新。
通过首先构建站点移动,您可以从一开始就考虑这一点,构建一个用户可以轻松上下滚动的线性页面,然后对于较大的视窗,您可以逐步增强页面。通过逐步添加视差效果,它使您能够建立一个响应视差网站。
社会的
随着社交网络越来越受欢迎,有一种趋势是包括按钮,使用户能够通过点击或触摸按钮来与他们的朋友和追随者共享内容。有大量这些不同的按钮可用于共享内容。值得注意的包括脸书的“喜欢”按钮,Twitter 的“Tweet”按钮,Google+“加一个按钮”,以及 Pinterest 的“Pin”按钮。
分享你的内容只是用户浏览你的网站的一小部分,然而,值得考虑的是,你的用户如何与他们互动可能会因他们使用的设备而有所不同。事实上,最近在 Marketing Land 2 上的一个帖子谈到了几乎两倍的社交份额来自移动设备。这意味着开发人员需要确保按钮位于清晰的位置,并且大小合适(苹果的指南建议移动设备上的按钮最小大小为 40×40px)。由于可用空间有限,这在小设备上可能很难做到,尤其是有许多不同的分享按钮,所以选择与用户和网站主题最相关的社交网络很重要。
除了知道三分之二的社交网络分享可能来自移动设备之外,看看社交分享按钮的总体效果也很重要,因为三分之二的无仍然是无。最近,英国政府网站进行了一项试验,将社交共享链接纳入其页面。他们发现,十周后,只有 0.2%的页面浏览量导致用户在他们的社交网络上分享该页面。如果你接着查看试验数据的分类,就会发现内容类型和分享频率之间有明显的联系,最受欢迎的内容是在世界定位页面上分享的(0.58%),最不受欢迎的是咨询信息(0.06%)。
适应用户旅程的总结
查看了一些常见的用户交互示例后,您现在应该对可能不适用于所有视口大小的用户交互类型有所了解。记住这一点,您现在应该能够在需要的地方调整这些交互,以便更好地适应不同的视口大小。
用户测试你的响应网站
通常,开发人员必须对我们的用户做出许多明智的假设,然而,不可能总是正确的,通过了解我们的用户,我们可以不断改进我们的网站。了解网络用户的最好方法是通过用户测试,让真正的用户使用你的网站,看看他们如何与网站互动。
等到你的网站开发过程结束才开始思考甚至做用户测试,为时已晚;重要的是从早期阶段你就知道你能在你的网站上使用什么样的测试。你可能听说过“最低生存产品”这个术语;这基本上是你的基本网站,没有多余的装饰,但是它是功能性的,并且已经安排好了用户的旅程。从本质上来说,网站应该处于这样一个阶段:如果你开始使用它,它仍然会为用户工作。当你有了这个最低限度的可行产品,你就可以开始让用户测试网站,这样你就可以开始得到一些关于用户旅程的不同部分是如何工作的反馈。
从用户测试中你可以学到一些重要的东西:
The different journeys the user takes through the site, so you can then compare this against how you originally thought the user would go through the site and learn from it. Where the user faces difficulties using the site. What the user likes about the site, which can guide how you develop the site going forward as you continue to use similar ways of displaying content.
您可能已经在以前的站点上使用了用户测试,但是,当您开始在一个响应站点上使用用户测试时,它会变得更加困难,因为它是为跨各种不同的视口工作而构建的。这意味着,在现实中,测试一个响应站点更像是测试几个站点,每个站点针对不同的设备,而不是简单地测试单个站点。当你为不同的用户提供不同的旅程时尤其如此。
进行用户测试
有许多不同的方法来执行用户测试,但是,所有的方法都有不同的优缺点,所以在做出决定之前,权衡每种方法的利弊是很重要的。
小组讨论
焦点小组是你聚集一群个人,最好不超过十个,他们是你的目标市场的一部分,让他们测试网站。测试完网站后,你可以让他们和一个主持人组成一个小组,讨论他们对网站的想法。焦点小组的目的是收集各种不同的人对网站工作方式的想法。
经营焦点小组有很多好处,最主要的好处是他们能让你了解用户对你网站的看法。为焦点小组选择的人来自目标市场,所以这将让你对你的目标用户有一个概念,包括他们觉得容易的和他们觉得困难的。
此外,在焦点小组中,在主持人的指导下,用户可以讨论他们对网站的想法。这允许用户基于彼此的答案,解释他们同意和不同意小组中其他人的地方。有了这种格式,你就可以平衡个人用户对网站的任何极端观点,否则这些观点可能会扭曲焦点小组的结果。作为焦点小组的一员,主持人有责任防止不必要的偏见,并且作为其中的一部分,他们需要负责确保焦点小组的一名或多名成员不会主导回答。相反,你要确保你得到的回答在整个团队中是平衡的。
当谈到在一个响应性网站上运行焦点小组时,重要的是你的用户都在讨论同一件事。这就是为什么对于每个站点断点,您需要运行一个单独的焦点组。通过这种方式,您可能会有一个针对移动体验的焦点小组,一个针对平板电脑体验的焦点小组,以及一个针对桌面体验的焦点小组。除此之外,你可能希望你的焦点小组专注于对你的网站至关重要的特定用户旅程,如果是这样的话,你应该要求你的焦点小组成员把他们的注意力放在那里。
使用焦点小组测试网站有许多缺点,主要是与运行焦点小组相关的成本。你不仅需要补偿焦点小组成员的时间,还需要在焦点小组会议期间雇佣一名独立的主持人来主持会议。你还需要考虑焦点小组不是在自然环境中;我的意思是,用户在使用网站时通常会被观察到,这可能会让他们感到不安,不太可能坦率地表达他们对网站的感受。
此外,在向焦点小组成员提问时,主持人可能会以影响答案的方式提问,从而引入偏见。因此,在会议之前审查问题以确保措辞不会扭曲答案是非常重要的。
可用性测试
可用性测试是目标市场成员和服务商之间一对一的对话。辅导员的角色是引导用户完成您希望他们执行的任务,然后分析用户在每项任务上的表现。
与焦点小组类似,您需要在您设置的所有不同响应断点上运行可用性测试。不幸的是,让每个用户测试站点的所有不同状态可能会引入不必要的偏见,所以您需要让不同的用户测试每个状态。
使用可用性测试的主要优点是,使用这种一个用户,一个主持人的方法,主持人可以观察用户使用网站时遇到的困难,这些困难在焦点小组环境中可能不会出现。与此类似,用户面临的在焦点小组中提出来可能会令人尴尬的困难可能会更容易与主持人讨论,而没有其他人在场听取。
除了主持人在用户使用网站时观察他们之外,记录会话也是值得的,这样你可以在以后回去更详细地回顾用户面临的困难。这种重新观看会话的能力意味着可能被忽略的问题也可以被修复。
类似于运行焦点小组,运行可用性测试也有相关的成本。对于你运行的每个可用性测试会议,你需要补偿用户和主持人的时间。可用性测试是一个一对一的会议,这比组织焦点小组更昂贵,因为你必须为每个参加会议的用户付费。
朋友、家人和同事
如果焦点小组和可用性测试由于成本原因都不是合适的选择,很可能你认识的朋友、家人,甚至同事会很乐意浏览你的网站并给你他们的想法。
你对这些用户进行测试的过程是让他们坐在设备前,让他们使用你的网站,可能是给他们一个简单的任务,让他们在使用网站的过程中完成。在测试过程中,你应该注意他们完成给定任务需要多长时间,因为这将帮助你确定旅程是否过于复杂。
除了记笔记,如果你有摄像机,你可以拍摄测试网站的人,特别是确保摄像机可以看到屏幕。这意味着您可以在以后回放视频,以防记笔记时遗漏了什么。
朋友和家人可能是测试你网站的宝贵资源,但是,重要的是要考虑他们是否是你网站的目标受众,甚至是你网站试图销售的产品,因为你可能最终会得到错误的结果。你也可能会发现,由于他们与你的关系,他们过于挑剔或不够挑剔。这些问题可能会扭曲向他们寻求帮助所获得的结果。
同事很可能知道你的产品,并有可能成为目标受众,但这可能意味着他们有投资兴趣,这影响了他们在测试网站时的判断。
除了这些问题,朋友、家人和同事是用户测试最便宜的选择,尽管你可能会发现有些结果需要保留,但你也有希望得到一些反馈,可以用来改善网站的用户体验。
网络分析工具
当谈到建立一个响应用户的旅程时,能够衡量这个旅程如何影响用户是很重要的,这样你就可以改善这一点。在您做出任何改进之后,您可以衡量它对用户体验产生了积极还是消极的影响。
衡量用户旅程的主要方法是跟踪网站的有效性。我的意思是通过观察用户使用网站的时间长度,他们在只看了一页后离开网站的可能性,以及他们通过网站的流量。衡量有效性可能真的很重要,例如,在企业的情况下,他们希望确保网站实现其将网站用户转化为客户的目的。您还需要能够衡量为改进网站而对网站进行的更改对网站产生了积极还是消极的影响。最好的方法是通过使用网络分析工具。
网络分析工具是可以安装在网站上的工具,使您能够测量网络流量,以评估和提高网站的有效性。他们通过跟踪用户如何使用网站来做到这一点,跟踪的类型取决于所使用的个人分析工具。
有许多可用的网络分析工具,其中一些是完全免费的,一些是收费的。我将在这里重点介绍的两个分析解决方案是 Google Analytics 和 ClickTale,这两个解决方案提供了非常不同的功能集,可以同时使用来帮助您了解更多关于您的用户的信息。
谷歌分析
Google Analytics 是一个免费的分析网站,可以让你追踪网站用户的信息。这些信息是完全匿名的,但是它可以给你一个明确的指示,告诉你访问你的网站的用户的类型。您可能以前在您的网站或您的一些客户网站上使用过 Google Analytics,但是,您可能没有做过的是深入到统计数据中查看访问您的网站的设备类型,或者使用它来查看您的用户在您的网站上的旅程。
对开发者来说,非常重要的一件事是了解我们网站的访问者使用的浏览器和设备。虽然 StatCounter ( http://gs.statcounter.com/
)提供的全球统计数据很好地展示了用户的总体使用情况,但这可能与您在自己的网站上体验到的情况有很大差异。这样做的主要原因是网站的目标受众对他们使用的浏览器和设备有很大的影响。例如,支持政府组织的公司可能有大量用户来自一个特定的浏览器,因为这是政府选择使用的浏览器。这就是像 Google Analytics 这样的分析工具可以为开发者增加价值的地方,因为他们能够记录用户正在使用的技术。
当涉及到提供关于浏览器的信息时,谷歌分析可以提供很多。要访问 Google Analytics 的浏览器部分,您需要点击“受众”菜单项,然后点击“技术”,再点击“浏览器和操作系统”。这将显示一个表格,列出所有访问该网站的浏览器,按最流行的排序。包括从该浏览器访问的用户数量信息以及他们的行为信息。如果你看一下我在图 8-6 中展示的谷歌分析工具的截图,你会注意到还有一个额外的选项,可以通过添加第二个维度来进一步过滤这些信息,或者你可以选择单击浏览器的名称来了解更多关于访问你的网站所使用的浏览器的具体版本的信息。图 8-5 显示了用来查看我的网站的浏览器和操作系统的分解视图。
图 8-5。
Breakdown of different browsers being used to access my site in Google Analytics
知道哪些浏览器被用来访问你的网站是非常重要的,因为你想知道哪些浏览器你需要支持和测试你的网站。但是,因为您正在开发一个响应站点,所以了解哪些设备正被用来访问站点也很重要,因为这将使您能够确保您的站点在访问站点时最常用的设备上进行测试。
谷歌分析的设备信息有两个部分;首先,是概述页面。要访问 Google Analytics 的浏览器部分,您可以单击受众菜单项,然后单击移动设备,然后单击概览。在本节中,您将看到按设备类别分类的设备,目前包括台式机、移动设备和平板电脑。这使您能够很好地了解用户正在使用的设备类型。定期检查这些数据非常重要,因为新设备类型的当前采用率可能会频繁变化。
谷歌分析的设备信息部分的第二部分允许你确定你的用户使用哪些设备访问你的网站。要查看这些信息,请访问“设备”页面中“移动设备”下的“受众”部分。在这个页面中,您可以很容易地看到访问您站点的不同设备,包括来自每个设备的用户百分比的统计数据。图 8-6 中的截图向您展示了设备视图,默认情况下,该视图显示按每台设备的访问次数排序的设备,最常见的设备显示在最前面。谷歌在这个视图中包含了一个小彩蛋,如果你点击设备名称旁边的小相机图标,你会看到设备的图像。图 8-6 显示了我的网站的谷歌分析设备列表视图。
图 8-6。
Breakdown of the devices used to access my site as shown in Google Analytics
了解用户使用的浏览器和设备对开发人员来说非常重要,但是正如本章所强调的,开发人员也需要考虑用户浏览网站的过程。规划你认为用户将如何浏览一个网站是很重要的,但是你也需要能够衡量这一点,看看你的期望是否与现实相符。这就是谷歌分析行为流工具特别有用的地方。
如图 8-7 所示,行为流工具从左侧开始,显示用户到达我的网站时所登陆的页面。向左移动可以显示用户继续浏览网站的位置,下降的是在该点离开网站的用户数量。
图 8-7。
The Behavior Flow of a site shown in Google Analytics
在了解了用户如何使用你的网站后,你可能会想尝试做些改变来改进它。很难确定对网站的改进会给用户带来更好的体验;然而,Google Analytics 能让你做的是创建一个实验,向用户展示一个页面的多个版本之一。这与脸书和 Twitter 等社交网络向一小部分用户推出新功能,以试验用户如何与他们互动的方式类似。然后,成功的实验会在整个站点铺开,不太成功的实验会被丢弃或查看,以了解它们出错的原因,并在做出更改后重新运行。
当在 Google Analytics 中使用实验时,您可以点击 Create experiment,这将带您进入实验创建屏幕。我的网站的实验列表页面如图 8-8 所示。
图 8-8。
The experiments listing page in Google Analytics
然后,系统会提示您输入实验的名称,定义一个目标,然后选择要用于实验的流量百分比。重要的是要注意,你选择的流量百分比完全取决于你在进行实验时期望你的网站获得的访问者数量。你要保证你从实验中得到足够好的样本量,但是,你也要保证你把实验的风险降到最低。百分比越低,风险越低。还有一些额外的高级选项,允许您定义实验将持续多长时间,以及如何在实验期间分配流量。图 8-9 显示了如何创建实验。
图 8-9。
Creating an experiment in Google Analytics
在您设置了实验的目标之后,您可以单击“下一步”按钮,并且您将能够开始配置它。要配置实验,请输入您想要实验的原始页面的 URL,然后您可以添加多达九种页面变体(图 8-10 )。您可以为每个变体命名,并且需要包含一个直接指向每个变体的链接。
图 8-10。
You can configure multiple variations for your experiment
设置好变体并单击“下一步”按钮后,您可以选择手动将实验所需的代码插入到您的网页中,或者通过电子邮件发送给网站管理员。因为您是开发人员,所以您只想从文本框中复制代码(如图 8-11 所示)并粘贴到您的页面中。
图 8-11。
The final step of creating an experiment outputs the script to add to your page
将代码添加到您的 web 页面之后,您现在可以单击 next step 按钮,这将验证代码是否已经就绪,并允许您开始实验。然后,您可以定期查看页面的不同版本的执行情况。
在 Google Analytics 中使用实验可以非常有效地让你测试如何改善用户体验的新想法。如果使用正确,这些工具可以让你继续改进你的网站,让你知道哪些改进用户喜欢,哪些不喜欢。
点击式
ClickTale 是一种不同类型的分析工具,专注于衡量有多少访问者访问了一个网站,而不是关注访问者的行为。
ClickTale 的核心功能之一是它能够记录用户如何与网站交互,这是通过跟踪用户在页面上执行的操作并记录下来实现的。这对开发者来说意味着,我们可以很容易地看到用户在浏览网站时的困难所在。
在图 8-12 中,正在播放一个 ClickTale 会话。您可以完全控制如何播放用户的会话,能够播放、滚动视频以及以不同的速度回放。
图 8-12。
Playback of a ClickTale user session
除了记录浏览网站的用户,ClickTale 还可以利用这些数据生成热图。热图是数据的图形表示,显示数据中的相关性,在这种情况下,这就是用户如何与网站交互。ClickTale 允许您查看一系列不同的热图,每个热图关注不同的指标:鼠标移动、用户点击的位置、用户关注的位置以及用户通常在页面上滚动到的位置。
要在 ClickTale 中查看热图,只需从主仪表板中选择您想要查看的热图。在图 8-13 中,我选择了鼠标移动热图,正如所料,您可以看到用户移动鼠标的主要区域是主导航。您还会注意到,在导航上的链接周围有一个小方框,下面有一个百分比。这是点击这些链接的用户的百分比。
图 8-13。
The heatmap view of ClickTale
与谷歌分析类似,ClickTale 允许你观察用户如何浏览网站,这是通过他们的转换漏斗实现的。ClickTale 的实现相对于 Google Analytics 的好处在于,它允许你更容易地进行过滤,而不必进行大量的配置,并且它专注于在达到最终目标之前查看用户退出你的网站的位置。界面也更简洁,更容易使用。
要开始使用转换漏斗(如图 8-14 所示),请转到仪表板并向下滚动到转换漏斗部分,单击查看/编辑漏斗链接,或者使用侧栏菜单并选择转换漏斗。
图 8-14。
The ClickTale conversion funnel
虽然 ClickTale 是一项商业服务,但它提供了一个能够记录 5000 次浏览量的免费账户。尽管这只是一个小样本,但您可以通过向 ClickTale 提供您预计会收到多少浏览量的估计来使它更加公平,这样它就可以记录一个随机样本。例如,如果你预计有 50,000 次浏览量,那么 ClickTale 可以被设置为记录这些用户中的十分之一。
摘要
我相信管理用户体验不是一门科学,而更像是一门艺术——因此,做事没有严格的对错之分。这一章的目的仅仅是引导你开始塑造你的响应站点的旅程。值得注意的是,虽然讨论的许多内容都可以作为构建响应之旅的起点,但用户的最佳体验是围绕他们试图通过您的网站实现的目标而定制的。
网站的内容是网站的核心,当你了解到用户感兴趣的内容时,你不应该害怕改变它。您可以通过使用用户测试和这里讨论的分析工具来测量用户感兴趣的内容,然后进行调整和再次测量,看看您是否做出了任何改进。
当开发你的用户之旅时,重要的是要记住你不一定第一次就 100%正确,这没关系;这是构建良好用户体验的本质。然而,你需要确保的是,你把你在船上学到的东西反馈到改进和发展你的网站上。
响应式设计是一种相对较新的技术,它可能会非常强大,但正如网站开发中的所有新事物一样,它将如何影响用户体验还有许多未知因素。这就是为什么恰当地衡量你的站点完成目标的程度是非常重要的,这样你就可以迭代和改进它。
作为这一章的一部分,我解释了你可以分析你的网站的用户旅程的方法,特别是看看你如何使用用户测试和分析工具来进一步改善你的网站。本章中解释的测试用户旅程的两种方法在你的站点中都有一席之地,它们可以很好地相互补充。
下一章将介绍如何使用响应式 JavaScript 技术将本章讨论的一些技术应用到一个站点上。
Footnotes 1
http://bradfrostweb.com/blog/mobile/fixed-position/
。。
2
http://marketingland.com/when-it-comes-to-social-media-sharing-mobile-rules-52750
。
3
https://insidegovuk.blog.gov.uk/2014/02/20/gov-uk-social-sharing-buttons-the-first-10-weeks/
。
九、跨响应状态的 JavaScript
使网站具有响应性的最常见方法是在样式表中使用媒体查询。媒体查询定义了浏览器显示这些特定样式所必须满足的规则。然而,使用媒体查询的主要限制是,它们只能用于改变站点的外观和感觉,因为它们在改变网站功能的方式方面只有有限的用途。为了正确响应网站支持的不同类型设备之间的差异,并提供真正的响应体验,必须结合样式表中的媒体查询使用其他技术。
对功能改变的潜在需求将由设备上可用功能的差异和它提供的视窗的大小来确定。设备之间的差异可能意味着不同设备上的用户可能会以完全不同的方式与您的网站进行交互。特别是,不同的输入方法,如触摸屏、电视遥控器、键盘和鼠标交互,都需要以不同的方式进行整合,以允许各种设备访问最高级别的网站功能。
为了满足跨响应状态更改功能的需求,您需要学习如何使用 JavaScript 来响应浏览器。甚至当我坐在这里写这一章的时候,我意识到用户体验设计师和开发者都在思考可以添加到网站中的新交互。在试用时,您可能会发现这些交互在您想要支持的各种设备上不能很好地工作,您可能希望能够调整这些功能。因为不可能涵盖您可能发现自己正在实现的每一个交互,所以本章着重于用必要的工具武装您,使您能够通过使用渐进增强技术来处理变化。
本章将探讨:
Different functionality across responsive states Techniques for changing functionality Implementing responsive JavaScript techniques
不同响应状态的不同功能
在响应式设计出现之前,当构建网站时,开发人员的目标是在他们想要支持的各种浏览器上提供相同的网站功能。在这不可能的地方,他们可能会尝试给老版本的浏览器提供降级的体验。然而,开发人员可以依赖的是,用户可能会使用键盘和鼠标与网站进行交互,因为用于控制网站的输入方法的数量非常有限。知道用户只会用键盘和鼠标与站点交互意味着开发人员可以优化这种体验,一个例子是当用户悬停在页面的特定元素上时添加功能。
随着新设备(特别是智能手机)进入主流,开发人员已经看到越来越多的用户开始与网站交互的输入方法。智能电视的触摸屏和简单遥控器等新的输入方法大大拓宽了旧的输入范围。因此,开发人员现在需要在为网站添加功能时考虑这些新的输入法。因此,开发人员需要考虑一些他们已经开始依赖的用户交互,比如悬停、鼠标进入和鼠标离开,在触摸屏上是行不通的。
除了不同的用户输入方法之外,还有越来越多的功能可以在为其构建网站的设备上使用。在这些功能中,越来越常见的是具有支持功能的设备,如摄像头、地理定位、运动和不同的设备方向。
如果我们放眼未来,设备会有许多新功能。一个这样的新 API 是振动 API,它通知用户应用程序中的变化,或者提供类似于游戏控制台如何通过振动提供反馈的反馈。
凭借所有这些新功能,它使开发人员能够添加桌面浏览器无法实现的新功能。这些技术需要使用渐进增强来实现,因为并非所有设备都是生而平等的。因此,尽管您可能已经在使用媒体查询逐步增强您的站点,但您也需要开始用 JavaScript 来做这件事。
第八章着眼于不同的、常见的用户交互,目的是探索如何使它们更好地工作。第八章提供了几个常见用户交互的例子,其中五个需要对用 JavaScript 构建的功能进行修改:
- 模态窗口
- 产品搜索
- 选项卡式容器
- 下拉菜单
- 视差
改变功能的技术
在第八章的中,我们已经探讨了为什么要根据不同设备的功能来改变它们的功能,现在让我们来看看如何实现这些功能上的改变。
可用于更改功能的技术可以分为两类:功能检测和状态管理。对这两种技术都有很好的理解是很重要的,这样你就知道什么时候可以单独使用或者一起使用来获得想要的结果。
特征检测
媒体查询极其受限的主要领域之一是检测浏览器的特征。虽然媒体类型可以在很小程度上用于确定用户使用的设备类型,但是有许多设备具有大量不同的可用功能,所有这些功能都响应媒体类型“屏幕”这意味着你不能依赖媒体类型来给你任何关于一个设备可能有哪些功能的想法。类似地,虽然媒体查询可以检测视口的大小或设备像素密度,但它们不能检测 JavaScript APIs 或大多数 HTML5 功能。考虑到这一点,您需要找到其他方法来检测这些特性,这可以在 JavaScript 中完成。
使用 JavaScript,您可以通过编程来检测某个特性是否受支持,并且可以构建一个网站来做出适当的响应。根据您想要检测的功能类型,可以采用不同的技术来确定它是否受支持。了解检测特性的不同方法很重要,所以让我们来看看可以使用的不同方法。
全局对象的一部分
检测一个特性是否被支持的最简单的方法是它是否被暴露在全局对象上(例如,window
对象)。如果它属于window
对象,那么该特征被支持。
应用这种技术的一个例子是测试浏览器是否支持 localStorage API。您可以简单地检查window
对象上的localStorage
是否存在,如以下代码所示:
var hasLocalStorage = function(){
return 'localStorage' in window;
}
元素的一部分
相反,如果您想要测试对作为 HTML5 规范的一部分添加的特定元素的支持,您将需要创建一个元素并测试该元素的特定特性。
可以使用这种方法测试的元素的一个例子是Canvas
元素。您首先需要创建一个虚拟元素。在本例中,您将把它存储在一个名为elem
的变量中。然后,您可以通过尝试在元素上调用特定于画布的方法来测试元素对画布 API 的支持;在这种情况下,您将使用getContext
。通常调用getContext
时,如果支持 Canvas,会得到一个 Canvas 渲染上下文,否则会得到 undefined。要将其转换为布尔值,只需使用两个感叹号(!!)放在elem.getContext
之前,如果支持 Canvas,则该方法返回 true,否则返回 false,如下面的代码所示:
var hasCanvas = function(){
var elem = document.createElement('canvas');
return !!(elem.getContext)
}
仅仅知道一个元素被支持并不总是足够的;HTML5 视频元素就是一个例子,它在不同的浏览器中支持不同的视频格式。如果您动态添加视频,将所有不同的格式添加到页面中是没有意义的,因此,您需要能够确定浏览器支持哪些视频格式。这方面的一个例子是包括 iPad 和 iPhone 上的 Safari 在内的 Webkit 浏览器支持的 H264 视频格式。要测试对 H264 的支持,您首先要测试方法canPlayType
,如果它受支持,那么您可以使用该方法来测试对 H264 编解码器的支持。
需要注意的是来自canPlayType
的值将返回一个具有三个可能值的字符串:可能,也许,或者一个空字符串。考虑到这一点,您需要将变量类型转换(更改变量类型)为 Boolean,这样它就可以返回 true 或 false。为了实现这一点,您可以使用双感叹号(!!)之后再返回值。第一个感叹号将根据值是真还是假进行类型转换,然后取其倒数。然后,第二个感叹号将再次反转该值,因此它与预期的一样。以下代码说明了这一点:
var hasVideoH264 = function(){
var elem = document.createElement('video');
if(!!elem.canPlayType){
return !!(elem.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'));
}
return false
};
检查值是否被保留
有时,您希望测试旧元素的新功能,您可以通过向属性添加一个值并测试浏览器是否保留该值或退回到其他受支持的内容来实现这一点。
一个使用它的例子是测试浏览器是否支持新的输入类型。您可以通过将属性类型的值设置为email
,然后测试浏览器是否忽略这个新值。在忽略该值的情况下,浏览器会默认将输入类型设置为text
。以下代码说明了这一点:
var hasInputEmail = function(){
var elem = document.createElement('input');
elem.setAttribute("type","email");
return elem.type === "email";
}
使用图书馆
看了编写测试之后,您可以很容易地看到,测试对特定特性的支持是相对简单的。当你需要测试大量的特性时,问题就来了,因为在不同的浏览器上编写和测试每一个特性是非常耗时的。除了编写自己的测试,您还可以选择使用一个包含各种不同测试的库,这些测试可以在您的代码中使用。一个这样的库是 Modernizr。
Modernizr 是由 Faruk Ateş创建的功能检测库,由一个开发人员社区开发。Modernizr 的目标是允许您测试各种各样的特性。特性检测可以以两种不同的方式使用:首先,它可以在 CSS 中使用,其次,它可以在您的 JavaScript 中使用。
通过检查存储在 Modernizr 对象上的值,可以在 JavaScript 中非常容易地使用 Modernizr。这意味着您可以轻松地将与特定浏览器特性相关的代码包装在条件语句中,以便在运行 JavaScript 之前检查支持。使用 Modernizr 检查地理定位 API 支持的一个例子是:
if(Modernizr.geolocation){
//JavaScript specific to the geolocation API
}
Modernizr 还允许您对 CSS 中不同特性的可用性做出反应;它通过向 HTML 元素添加类来实现这一点,您可以从 CSS 中使用这些类来向您的站点添加样式,以便使用特定的功能:
.geolocation .local-search{
background: #666 url('geosearch.png') center center no-repeat;
width: 40px;
height: 40px;
}
除了为浏览器支持的特性添加类,Modernizr 还为它不支持的特性添加类,这些类带有前缀no-
。您可以使用这些带前缀的类来隐藏部件,或者为您选择在您的站点上使用的任何不受支持的功能提供替代或后备。一个例子是:
.no-geolocation .local-search{
display: none;
}
这使您能够逐步增强您的站点,充分利用用户浏览器提供的功能,同时为浏览器不支持这些功能的用户提供支持。
基于特征的动态加载
我已经解释过,您可以通过使用条件语句简单地检查一个特性来对其做出响应,然后如果它是真的,您可以添加或删除特定的功能。但是,在某些情况下,当某个功能可用或不可用时,您可能希望加载附加的 JavaScript 库或聚合填充。例如,对于不支持 window.matchMedia API 的设备,您可以有条件地加载 polyfill。要处理 JavaScript 的这种有条件加载,您可以使用一个名为 yepnope.js 的 JavaScript 库。该库允许您提供一个测试,然后根据该测试的通过或失败提供一组您想要加载的 JavaScript 文件。
要使用 yepnope,只需将带有参数的对象传递给yepnope
方法。yepnope.js 方法支持的参数有:
test
: The test you want to use, this should be either true or false, however you can use a method if you immediately run it. yep
: An array of JavaScript files to load if the test passes. nope
: An array of JavaScript files to load if the test fails. both
: An array of JavaScript files to always load for both yep and nope. load
: An array of JavaScript files to always load (similar to “both”). callback
: A callback for after the file has loaded. complete
: A callback for after all files have loaded.
如果您想了解如何使用 yepnope.js 来测试是否支持 matchMedia API,并在不支持的情况下加载 polyfill,您需要提供一个测试,并将 polyfill 添加到传递给nope
参数的数组中:
yepnope({
test : function(){
if(typeof window.matchMedia === "function"){
return true
}
else{
return false;
}
}(),
nope : ['matchMedia.js']
});
这样,如果 window.matchMedia API 不可用,您现在可以有条件地加载聚合填充。
国家管理
虽然能够基于浏览器的功能集有条件地运行代码和应用 CSS 确实非常强大,但有时您需要管理基于浏览器状态运行的代码。
当您第一次想到浏览器状态时,您可能会想到关键视口大小。通过这种方式思考,你可能会猜测有四种不同的状态:超小、小、中和大。然而,浏览器状态的定义可以进一步简化为视口在任意给定点的大小。这意味着当谈论状态时,不要从关键视口大小或设备的角度来考虑它们,而应该从代码所针对的一系列维度来考虑它们。
理解什么是状态可以让你正确地思考你需要做什么来管理你的状态。管理状态时,您需要做的关键事情是如何激活、停用和管理不同状态之间的转换。例如,当状态被激活时,您可能想要添加一项功能,而当状态被停用时,您可能想要移除该功能。
有许多方法可以使用浏览器提供的 API 来管理浏览器状态。我将解释的两个 API 中的第一个是 window.matchMedia API,这是一个与媒体查询一起引入的新 API,允许您在 JavaScript 中测试媒体查询。我要解释的第二个 API 是 window.onresize API,虽然它最初不是为响应式设计而设计的,但正如我将要解释的,它可以用于管理响应式状态。
为了正确理解如何使用这些 API,让我们分别研究一下,看看每种方法的好处。
window.matchMedia
使用 window.matchMedia API,您可以测试单个媒体查询,以检查它是否与当前浏览器状态匹配。您在 API 中使用的媒体查询与您已经了解的在 CSS 中使用的媒体查询是相同的。
使用这个 API 最简单的方法是检查媒体查询当前是否匹配。这可以通过调用window.matchMedia
并将想要测试的媒体查询作为参数传递来实现。然后,API 将返回一个 MediaQueryList,这是一个包含几个属性的对象,其中一个属性是matches
,当查询时,它的值将为 true 或 false。如果您将这些放在一起作为一个简单的条件语句来检查媒体查询是否匹配,它将类似于下面的代码:
if (window.matchMedia("(max-width: 767px)").matches) {
// the viewport is a small device
} else {
// the viewport is a larger device
}
虽然能够测试媒体查询当前是否匹配非常有用,但 window.matchMedia API 的真正强大之处在于它能够将侦听器添加到使用媒体查询创建的 MediaQueryList 中。如果您将侦听器添加到 MediaQueryList,它允许浏览器在媒体查询匹配或不匹配时通知您,从而允许您做出适当的响应。
我已经解释过,将媒体查询作为参数传递将返回一个 MediaQueryList 对象,该对象具有许多属性。为了使您能够在必要时使用这些属性,而不是直接从 API 调用中调用它们,您可以将 MediaQueryList 存储在一个变量中:
//Create match media list
var mql = window.matchMedia("(max-width:767px)");
很容易看出这个 MediaQueryList 对象在浏览器控制台中的样子,您只需在浏览器中运行这段代码,然后检查您定义的变量的值(见图 9-1 )。
图 9-1。
The media query list object as shown in the browser console
从查看控制台中的对象可以看出,MediaQueryList 提供了通过查看matches
属性立即查看媒体查询是否匹配以及添加和删除侦听器的能力。
要将侦听器添加到 MediaQueryList,可以通过传递一个listener
方法来使用 addListener。您定义的侦听器将把 MediaQueryList 作为第一个参数传递回来,然后您可以用它来检查用户定义的媒体查询当前是否匹配。这是通过使用前面讨论的matches
属性并检查值是否等于 true 或 false 来实现的。如果使用条件语句,可以很容易地将进入状态(匹配将等于真)与退出状态(匹配将等于假)区分开来。综合起来,listener
方法将如下所示:
//Create an the listener for mobile
var mobileListener = function(mqlObj){
if(mqlObj.matches){
console.log('enter mobile');
}
else{
console.log('exit mobile');
}
};
值得注意的是,虽然您可以向addListener
方法传递一个匿名函数,但是在添加侦听器时,这样做有一个缺点。这是因为当您传递一个匿名函数时,当您稍后想要删除侦听器时,您没有一个参考点可以使用。
MediaQueryList 和listener
方法完成后,现在可以将listener
方法附加到 MediaQueryList。这可以通过简单地使用mql.addListener
,将监听器方法作为参数传递来实现:
mql.addListener(mobileListener);
如果将所有这些放在一起看,您会得到以下结果:
//Create match media list
var mql = window.matchMedia("(max-width:767px)");
//Create an the listener for mobile
var mobileListener = function(e){
if(e.matches){
console.log('enter mobile');
}
else{
console.log('exit mobile');
}
};
//Add the listener to the MediaQueryList
mql.addListener(mobileListener);
MediaQueryList 对象用于管理侦听器的第二个方法是removeListener
方法。此方法的目的是让您可以轻松地删除以前添加的侦听器。在添加侦听器之前,您将侦听器分配给一个变量;因此,要删除一个侦听器,您需要将这个变量传递给removeListener
,这将从 MediaQueryList 中删除该侦听器:
mql.removeListener(mobileListener);
到目前为止,我已经解释了如何在单个媒体查询中添加和删除侦听器。通常,这是不够的,因为您很可能需要管理各种不同的状态。因此,您可以选择设置多个 MediaQueryLists,将特定于状态的侦听器附加到每个列表:
//Create match media list
var smallMediaQuery = window.matchMedia("(max-width:767px)"),
mediumMediaQuery = window.matchMedia("(min-width:768px) and (max-width:991px)"),
largeMediaQuery = window.matchMedia("(min-width:992px)");
//Create an the listener for small devices
var smallListener = function(e){
if(e.matches){
console.log('enter small device');
}
};
//Create an the listener for medium devices
var mediumListener = function(e){
if(e.matches){
console.log('enter medium device');
}
};
//Create an the listener for large devices
var largeListener = function(e){
if(e.matches){
console.log('enter large device');
}
};
//Add the listener to the MediaQueryList
smallMediaQuery.addListener(smallListener);
mediumMediaQuery.addListener(mediumListener);
largeMediaQuery.addListener(largeListener);
在上面的示例中,我为每个媒体查询创建了一个 MediaQueryList,并为每个查询附加了一个不同的侦听器。或者,我可以创建一个共享侦听器方法,该方法将被添加到每个 MediaQueryList 中,但是,在这种情况下,我必须额外查询传递给侦听器的 MediaQueryList,以确定正在管理哪个状态。
正如您现在所了解的,您可以使用 window.matchMedia API 轻松管理您的状态,主要好处是您可以在 JavaScript 中重用您在 CSS 中使用的相同媒体查询。
值得注意的是,浏览器对 matchMedia API 的支持仅限于较新的浏览器,因此,如果您希望在早于 Internet Explorer 10 的浏览器中使用 matchMedia API,您将需要使用 polyfill。
window.onresize
已经了解了 window.matchMedia API,让我们看看如何使用 window.onresize API 来管理响应状态。这个 API 在 responsive design 之前很久就已经存在了,所以它最初不是用来管理响应状态的,但是,它非常适合这样做,因为它会在浏览器调整大小时触发,就像当用户调整浏览器大小时您希望如何切换响应状态一样。
考虑到这一点,让我们看看如何以响应的方式使用 window.onresize API。这里的例子将着眼于如何响应浏览器的视窗宽度。
对于这个例子,我将使用 Christian Heilmann 开发的启示模块模式,作为 Richard Cornford 的模块模式的改编。这是有意义的,因为它封装了代码。我不会深入探讨我正在使用的模式,但是,如果你想了解更多,Christian 在他的博客上写了一篇有用的文章。 1 例子从简单的代码开始:
var stateManager = (function(){
return {
}
}());
因为您需要知道您在任何给定时刻所处的状态,所以您需要在模块的顶部创建一个变量,您将使用该变量来存储当前状态的名称:
var stateManager = (function(){
var state = "";
return {
}
}());
当 resize 方法触发时,您需要检查浏览器宽度以确定当前状态是否已更改。考虑到这一点,您需要编写一个方法来确定浏览器的宽度。不幸的是,这并不像你想象的那么简单,因为我们发现不同浏览器的宽度是不一致的。
var getWidth = function () {
var x = 0;
if (typeof(document.body.clientWidth) == 'number') {
// Newer generation of browsers
x = document.body.clientWidth;
}
else if( typeof( window.innerWidth ) == 'number' ) {
//None Internet Explorer
x = window.innerWidth;
}
else if( document.documentElement && document.documentElement.clientWidth ) {
//Internet Explorer 6 and above in 'standards compliant mode'
x = document.documentElement.clientWidth;
}
return x;
};
有了这个检查浏览器宽度的方法,现在需要编写方法来处理 resize 事件。resize 事件的方法可以细分如下:
Check the width against different values to determine which state the browser is currently in. Determine if that state is currently active. If the state is not active, fire the relevant method and set the state to the name of the new state.
考虑到这种方法,该方法的代码如下所示:
var onResizePage = function () {
if (getWidth() < 768) {
if (state !== "small") {
//Enter mobile method goes here
state = "small";
}
}
else if (getWidth() >= 768 && getWidth() < 992 && state !== "medium") {
if (state !== "medium") {
//Enter tablet method goes here
state = "medium";
}
}
else if (getWidth() < 992) {
if (state !== "large") {
//Enter desktop method goes here
state = "large";
}
}
};
下一步是定义当您进入每个状态时将被调用的方法。因为您已经定义了三个状态,所以您将创建三个方法:enableSmall
、enableMedium
和enableLarge
。对于本例,只需将状态的名称记录到控制台:
var enableSmall = function(){
console.log('enter small);
};
var enableMedium = function(){
console.log('enter medium);
};
var enableLarge = function(){
console.log('enter large);
};
定义了进入状态时的方法后,您现在想要将对这些方法的调用添加到已经创建的onResizePage
方法中:
var onResizePage = function () {
if (getWidth() < 768) {
if (state !== "small") {
enableSmall();
state = "small";
}
}
else if (getWidth() >= 768 && getWidth() < 992 && state !== "medium") {
if (state !== "medium") {
enableMedium();
state = "medium";
}
}
else if (getWidth() < 992) {
if (state !== "large") {
enableLarge();
state = "large";
}
}
};
早先当你定义一个模块时,你定义一个返回值作为一个对象;现在,您将需要向该对象添加一个键值对,将键设置为init
,将其值设置为一个方法。在这个方法中,您将定义并运行onResizePage
方法,然后添加一个 resize 事件监听器,它将在浏览器调整大小时运行onResizePage
方法:
var stateManager = (function() {
var state = "";
var getWidth = function () {
var x = 0;
if (typeof(document.body.clientWidth) == 'number') {
// Newer generation of browsers
x = document.body.clientWidth;
}
else if( typeof( window.innerWidth ) == 'number' ) {
//None Internet Explorer
x = window.innerWidth;
}
else if( document.documentElement && document.documentElement.clientWidth ) {
//Internet Explorer 6 and above in 'standards compliant mode'
x = document.documentElement.clientWidth;
}
return x;
};
var onResizePage = function() {
if (getWidth() < 768 && state !== "small") {
enableSmall();
state = "small";
}
else if (getWidth() >= 768 && getWidth() < 992 && state !== "medium") {
enableMedium();
state = "medium";
}
else if (getWidth() < 992 && state !== "large") {
enableLarge();
state = "large";
}
};
var enableSmall = function() {
console.log('enter small');
};
var enableMedium = function() {
console.log('enter medium');
};
var enableLarge = function() {
console.log('enter large');
};
return {
init: function() {
onResizePage();
window.addEventListener("resize", onResizePage, true);
}
};
}());
完成状态管理器后,最后一步是运行它,只需运行返回的init
方法即可实现:
stateManager.init();
使用window.onresize
方法实现响应式状态的关键问题是,您必须自己做大量的工作。浏览器中的window.onresize
事件的目的是在用户调整浏览器大小时简单地触发一些 JavaScript,因此它没有响应或状态的概念。这意味着,在这个简单的例子中,您必须检查一个状态是否有效,并跟踪自己何时启用这些状态。
虽然能够使用window.onresize
方法编写一个响应状态管理器是针对不同响应状态的 JavaScript 的一个很好的解决方案,但为了正确,它可能比 window.matchMedia API 需要更多的时间来实现。记住这一点,不要让这些额外的工作影响你使用这种方法。由于所有主流浏览器都支持 window.onresize API,这种方法提供了最好的浏览器支持。
同样值得注意的是,如果使用 window.matchMedia polyfill,监听器事件将使用 window.onresize API 来 poly fill window . match media API。这意味着您可能需要跨不同的浏览器进行额外的测试,因为您的代码将根据是否支持 window.matchMedia API 以不同的方式工作。
图书馆
看了编写响应式 JavaScript 时可以使用的两个核心 API,您现在对两者的区别和功能有了很好的理解。您可能已经注意到的一件事是,两者都需要您编写大量的代码来管理您的状态。幸运的是,有许多可用的库,使您能够编写更少的代码,并在浏览器 API 的基础上提供更多的功能。
在这里,我将重点介绍两个 JavaScript 库:SimpleStateManager 和 enquire.js。这两个库对响应式 JavaScript 采取了不同的方法,但旨在解决开发人员在尝试针对不同的响应状态提供不同的功能时面临的相同的基本问题。
简单状态管理器
我将解释的第一个 JavaScript 库是 SimpleStateManager,它是一个构建在 window.onresize API 之上的响应性状态管理器。
在使用 SimpleStateManager 之前,您需要下载该库并将其添加到您的站点中。有两种方法可以将这个库添加到你的项目中:第一种也是最简单的方法是使用 Bower,你应该记得这是在第七章中讨论的包管理器。您可以使用 Bower 通过以下命令将 SimpleStateManager 添加到项目中:
bower install SimpleStateManager
一旦使用 Bower 下载了包,只需在页面中包含 JavaScript 文件:
<script src="bower_components/SimpleStateManager/dist/ssm.min.js"></script>
请注意,根据您配置 Bower 的方式,SimpleStateManager 的路径可能会有所不同。
如果你没有在你的项目中使用 Bower,将 SimpleStateManager 添加到你的项目中的另一种方法是直接从 www.simplestatemanager.com
下载这个库,并将它包含在你的页面中。
在项目中设置了 SimpleStateManager 之后,现在可以添加响应状态了。SimpleStateManager 中的状态是通过设置许多配置选项来定义的,包括定义在进入、离开状态和调整状态大小时要运行的任何回调。要在 SimpleStateManager 中添加状态,请使用ssm.addState
方法,该方法允许您使用一系列不同的选项来定义状态。SimpleStateManager 中支持的状态选项有:
id
(optional): The ID is the unique identifier, if provided, you can use this ID to query the state and later remove the state if necessary. minWidth
(optional): Allows you to define the minimum width that the state is active. maxWidth
(optional): Allows you to define the maximum width that the state is active. onEnter
(optional): Allows you to define a callback for when you enter the state. onResize
(optional): Allows you to define a method for when the browser is resized while the state is active. onLeave
(optional): Allows you to define a method for when you leave the state. This potentially could be used to clean up your state when you leave it.
重要的是要理解 SimpleStateManager 中的所有选项都是可选的,允许您以最适合您的项目的方式使用该库。既然您已经看到了创建状态时可用的选项,让我们添加第一个状态。第一个状态将针对移动设备,应用最大宽度 767,对于onEnter
、onResize
和onLeave
方法,您只需将状态当前正在做的事情记录到控制台。
当您设置好所有的状态后,您需要告诉 SimpleStateManager 您已经准备好让它应用这些状态了。这是通过使用ssm.ready
方法实现的,该方法不需要任何参数,只是简单地测试每个状态,看它们是否有效。对于任何有效的状态,将运行onEnter
方法。ssm.ready
的用法简单如下:
ssm.ready();
现在状态都设置好了,您可能会发现,在 JavaScript 的后面,可能作为处理用户动作的一部分,您想要确定某个特定的状态当前是否是活动的。这可以通过使用ssm.isActive
方法来实现,该方法接受一个参数,即您想要检查活动状态的状态的 ID。如果您要测试移动设备状态是否是活动的,您可能会希望使用条件语句。在本例中,如果移动状态当前处于活动状态,您将登录到控制台:
if(ssm.isActive('mobile')){
console.log('mobile is active');
}
有时状态可能变得多余,所以您需要删除它们;这是通过使用ssm.removeState
方法实现的。类似于ssm.isActive
方法,该方法接受一个参数,即您想要删除的状态的 ID:
ssm.removeState('mobile');
需要注意的重要一点是,removeState
方法只是从 SimpleStateManager 中移除状态,并不处理任何可能需要的整理工作。这是有意的,因为当你删除状态时,库不知道你的意图。在某些情况下,如果删除状态,您可能会发现需要触发onLeave
事件。虽然 SimpleStateManager 不会为您这样做,但是使用ssm.getStates
方法获得您要删除的状态并自己触发onLeave
方法是非常容易的。
向 SimpleStateManager 添加状态时,您并不局限于一次添加一个状态。您可以使用ssm.addStates
方法通过传递定义每个状态选项的对象数组来添加多个状态:
ssm.addStates([
{
id: 'mobile',
maxWidth: 767,
onEnter: function(){
console.log('enter mobile');
}
},
{
id: 'tablet',
minWidth: 768,
maxWidth: 991,
onEnter: function(){
console.log('enter tablet');
}
},
{
id: 'desktop',
minWidth: 992,
onEnter: function(){
console.log('enter desktop');
}
}
]);
使用 SimpleStateManager,您可以添加无限数量的状态,这些状态可以相互重叠。需要注意的是,尽管可以添加大量的状态,但这可能会影响性能,因此,合理使用添加的状态数量非常重要。
SimpleStateManager 真正突出的地方在于它能够向您的状态添加您自己的定制配置选项。这意味着您可以定义一个测试,然后在状态选项中设置一个测试值。
要添加自定义配置选项,您只需传递一个包含测试名称的对象(这将成为您传递给 state 的选项,因此建议您使用骆驼大小写字体),然后传递测试方法。然后,测试方法能够读取状态选项中设置的值,并测试是否满足条件。然后,该方法应该返回 true 或 false。如果您查看一个如何实现新配置选项的示例,它将是这样的:
ssm.addConfigOption({name:"maxHeight", test: function(){
if(typeof this.state.maxHeight === "number" && this.state.maxHeight >=document.documentElement.clientHeight){
return true;
}
return false;
}});
对于此配置选项,您将定义一个规则,该规则允许您在视口内设定视口的最大高度。如果您的浏览器视窗超过了状态选项中maxHeight
定义的高度,该特定状态将不会被应用或被停用。
要测试新的配置选项,只需将它添加到状态中,并像使用 SimpleStateManager 附带的默认配置选项一样使用它:
ssm.addState({
id: 'mobile',
maxWidth: 767,
maxHeight: 320,
onEnter: function(){
console.log('enter mobile');
}
}).ready();
测试了定制配置选项之后,您现在可以看到设置定制测试来针对您的状态运行是多么容易。这样做的真正好处是,您可以在 JavaScript 中测试的任何能够返回 true 或 false 值的东西都将作为一个配置选项,这意味着这不仅仅是简单地查询视窗,还可以用来测试浏览器是否支持某个特定的特性。例如,测试设备是否支持localStorage
:
ssm.addConfigOption({name:"localStorage", test: function(){
if('localStorage' in window){
return true;
}
return false;
}});
然后,您可以将它作为一个配置选项添加到您的状态中:
ssm.addState({
id: "localStorage",
localStorage: true,
onEnter: function(){
console.log('supports local storage');
}
}).ready();
如果没有必要,您甚至不需要提供任何宽度配置选项,并且可以将一组浏览器支持的功能作为一种状态。
除了添加自定义配置选项之外,还有另一种方法可以向 SimpleStateManager 添加功能——通过使用或编写自己的插件。类似于 jQuery 允许您编写自己的插件,SimpleStateManager 允许您编写扩展其功能的插件。您可能使用的 SimpleStateManager 插件的一个例子是带有 Colorbox ( http://colorbox.simplestatemanager.com/
)的 SSM,这是 SimpleStateManager 团队开发的官方插件之一。这个插件用一个包装器包装了 jQuery 插件 Colorbox,该包装器允许您在您的状态中启用和禁用插件。
在考虑 SimpleStateManager 是否适合您的项目时,重要的是要看它会给项目带来的好处。使用 SimpleStateManager 的主要好处是:
It removes the need to manage your responsive states manually, you simply add a state and the library manages the activation and deactivation. You can define enter, resize, and leave events for each of your states. You are able to extend SimpleStateManager with custom config options, allowing you to add custom tests to your states. There are a growing number of plug-ins for SimpleStateManager, allowing you to extend the library further. Through using the window.onresize API, the library does not require a polyfill to work in older browsers such as Internet Explorer 7, 8, or 9.
另一方面,使用 SimpleStateManager 有一个主要缺点。SimpleStateManager 使用配置选项来定义状态,您不能使用与您在 CSS 或 JavaScript 中使用的相同的媒体查询,而是必须为选项提供值。这只是使用 SimpleStateManager 可以做什么的一个简短示例。在 SimpleStateManager 网站的 www.simplestatemanager.com
上有完整的文档。
enquire.js
您可以使用的另一个库是 enquire.js,在这种情况下,它构建在 window.matchMedia API 之上,允许您编写响应性 JavaScript。它是作为 API 的包装器构建的,扩展了功能,使其更易于使用,并增加了在不支持 API 的浏览器中降级的灵活性。
在项目上开始使用 enquire.js 的第一步是下载库并将其添加到您的站点;这可以通过两种方式实现。第一种,也是最简单的,将这个库添加到你的项目中的方法是使用第七章中讨论的 Bower 包管理器。您可以简单地使用 Bower,通过以下命令将 enquire.js 添加到项目中:
bower install enquire
一旦使用 Bower 下载了包,只需在页面中包含 JavaScript 文件:
<script src="bower_components/enquire/dist/enquire.min.js"></script>
需要注意的是,enquire.js 库的路径可能会有所不同,这取决于您配置 Bower 的方式。
如果您的项目中没有使用 Bower,将 enquire.js 添加到项目中的另一种方法是直接从 http://wicky.nillia.ms/enquire.js/
下载库,并将其包含在您的页面中。
用 enquire.js 添加状态的方法是通过用enquire.register
方法注册它们。这个方法有两个参数,第一个是媒体查询,它定义了何时应用状态。第二个参数可以采用两种形式,一种是回调,它只是在媒体查询匹配时触发,另一种是包含状态的不同事件的对象。您可以传递给enquire.register
的选项有:
match
: Provides a method that will fire when the media query is matched. unmatch
: Provides a method that will fire when the media query is unmatched. setup
: Provides a method that will fire when the state is first registered. deferSetup
: Provides a method that will fire when the media query first matches. destroy
: Provides a method that will fire when the state is unregistered.
为这些事件提供回调是可选的,这允许您选择最适合用于状态更改的回调。让我们看看如何创建第一个状态。为此,您将利用之前学到的关于编写媒体查询的知识,编写一个针对移动设备的媒体查询。在这个例子中,您将使用媒体类型screen
和媒体表达式max-width
: 767px,当它们放在一起时被编码为screen and (max-width: 767px)
。您将把它作为第一个参数传递,对于选项,让我们传递一个match
和unmatch
方法,两者都记录到控制台。放在一起,看起来是这样的:
enquire.register("screen and (max-width: 767px)", {
match : function() {
console.log('enter mobile');
},
unmatch : function() {
console.log('leave mobile');
}
});
在您的浏览器中进行测试时,在您进入状态后,控制台将输出 enter mobile,当您离开该状态时,控制台将输出 leave mobile。如您所见,使用 enquire.js 添加单个状态非常简单,但是添加多个状态也同样简单,因为 enquire.js 是一个可链接的库,这意味着您可以像在 jQuery 中链接命令一样链接命令。记住这一点,让我们尝试通过链接.register
方法来添加多个状态。对于本例,我们不传递不同方法的对象作为第二个参数,而是简单地传递一个在状态匹配时使用的方法:
enquire.register("screen and (max-width: 767px)", function() {
console.log("enter mobile");
})
.register("screen and (min-width:768px) and (max-width:991px)", function() {
console.log("enter tablet");
})
.register("screen and (min-width:992px)", function() {
console.log("enter desktop");
});
到目前为止,您已经添加了状态,但是,有时您可能会发现您想要删除状态。这可以通过简单地将原始媒体查询传递给enquire.unregister
方法来实现:
enquire.unregister("screen and (max-width: 767px)");
成功移除一个状态后,让我们看看 enquire.js 是如何处理浏览器支持的。如前所述,enquire.js 所基于的 window.matchMedia API 只有较新的浏览器才支持。这意味着 Internet Explorer 7、8 和 9 将不支持该 API。但是 enquire.js 考虑到了这一点,并使您能够将您的体验降级为仅桌面体验。这是通过将true
作为第三个参数传递给桌面状态的 register 方法来实现的,告诉 enquire.js 如果不支持 window.matchMedia API,那么这个 stage 应该总是匹配的:
enquire.register("screen and (min-width:992px)", function() {
console.log("enter desktop");
}, true);
如果您想让您的测试在这些较旧的浏览器中工作而不是降级,您可以对 window.matchMedia API 使用 polyfill 来增加对较旧浏览器的 API 支持。我在前面解释 window.matchMedia API 时讨论过一个这样的 polyfill。
在考虑 enquire.js 是否适合你的项目时,重要的是要看它会给项目带来的好处。使用 enquire.js 的主要好处是:
It takes away the need to manage your responsive states manually, simply add a media query and relevant listeners. You are able to use the same media queries in your JavaScript as you are using within your CSS
另一方面,使用 enquire.js 也有一些缺点:
It does not provide a built-in plug-in API. It requires the use of a matchMedia polyfill in order to support older browsers such as Internet Explorer 7, 8, and 9.
这只是一个简短的例子,你可以用 enquire.js 做什么。在 enquire.js 网站上有完整的文档,网址是 http://wicky.nillia.ms/enquire.js/
。
对高级状态使用特征检测
到目前为止,我已经解释了如何使用特征检测来响应浏览器的特征;然而,我没有解释如何构建考虑到浏览器特性的状态。这意味着状态仅限于简单地响应视口宽度;虽然对于许多网站来说这可能已经足够了,但是理解为什么以及如何考虑州内的特性是很重要的。
作为开发人员,当我们构建响应式站点时,我们经常被迫对用户做出假设。我们通常通过视口来划分设备;一个例子是所有小型设备都是智能手机。这种假设可能会损害我们用户的体验,因为使用非智能手机的小设备的用户可能会获得更糟糕的体验。这是因为我们假设他们使用的是智能手机,而实际上他们可能只是调整了浏览器窗口的大小。这就是为什么当瞄准像触摸屏这样的功能时,它成为我们逐步增强网站的方式的一部分变得很重要。
如果您正在逐步增强您的站点以使用特定于设备的功能,您可能需要在您的州内测试这些功能。这是因为只有在特定视口状态和设备特定特征配对的情况下,才需要改变功能。这为您的状态增加了额外的复杂性,因为您不仅基于浏览器状态来定位功能,而且还基于可用的特性来分离功能。
继续智能手机的例子,让我们看看如何针对一个小设备的触摸屏。对于这个例子,让我们使用前面讨论过的 Modernizr 特征检测库。要确定设备是否为触摸屏,您可以在您的状态下检查Modernizr.touch
的值:
var smallState = function(){
if(Modernizr.touch){
console.log('small device with a touch screen');
}
else{
console.log('small device without a touch screen');
}
}
从这个例子中很容易看出,将使用状态与特征检测结合起来是多么简单。然而,仅仅通过添加一个条件语句来确定它是否是触摸设备,就使被测试设备的类型增加了一倍。你现在测试的不是“小型设备”,而是“支持触摸事件的小型设备”和“不支持触摸事件的小型设备”
这给原本简单的状态增加了更多的复杂性。这可能会变得更复杂的一个例子是,如果您想在测试 touch API 的同时测试地理定位 API,但需要根据它是否是触摸设备做出不同的响应:
var smallState = function(){
if(Modernizr.touch){
if(Modernizr.geolocation){
console.log('small device with a touch screen and geolocation');
}
else{
console.log('small device with a touch screen');
}
}
else{
if(Modernizr.geolocation){
console.log('small device without a touch screen but with geolocation');
}
else{
console.log('small device without a touch screen');
}
}
}
正如您所看到的,通过添加地理定位 API,您现在已经将可能发生的不同用例的数量增加了一倍。虽然这是可管理的,但重要的是要强调,虽然在状态管理中结合功能检测可以提供巨大的好处,但您需要意识到它会增加您需要管理的代码的复杂性。
如前所述,管理该代码的一个选项是使用 SimpleStateManager 库,它允许您添加自定义配置选项,并允许您添加功能检测。这也允许您在状态级别上配置您的功能检测,但是,缺点是您需要为您支持的每个不同的功能组合定义一个新的状态。
实现响应式 JavaScript 技术
到目前为止,我主要关注的是为什么您想要改变功能以及如何实现这一点背后的理论,但是一旦您理解了这一理论,那么理解如何将响应式 JavaScript 技术应用到您的网站就变得非常重要。
对于这个例子,让我们看看如何在一个站点上实现一个登录提示,它将出现在较小设备上的一个新页面上,而在较大设备上将出现在一个模态窗口中。为了简化这些例子,我将使用 jQuery 来处理一些 DOM 操作。如果您更喜欢使用原生 JavaScript 或其他库,请随意替换 jQuery 的用法。
作为起点,让我们以使用 Twitter Bootstrap 在第五章中构建的代码为例。这包含在第五章的代码包中。以此为起点建立项目后,您可以开始向导航添加一个新的超链接,以链接到登录页面。要实现这一点,只需在导航条上添加第二个导航。因为您希望这个导航位于导航栏的右侧,所以让我们添加类navbar-right
:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">Link</a></li>
<li><a href="#">Link</a></li>
<li><a href="#">Link</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="login.html" class="login-link">Login</a></li>
</ul>
</div>
一旦您添加了此链接,它就会出现在导航栏上。中型视口如图 9-2 所示。
图 9-2。
The navigation on the medium viewport
为了使您能够添加功能,请在页面底部靠近 body 标记的位置添加一个新的 JavaScript 文件,将其命名为 main.js。您还将使用 SimpleStateManager 来管理响应状态,所以我们也将它添加到页面中:
<script src="js/ssm.min.js"></script>
<script src="js/main.js"></script>
链接到登录页面并添加了新的 JavaScript 文件后,让我们创建您刚才链接到的登录页面。最简单的方法是复制 index.html 文件,将其命名为 login.html。
创建了 login.html 页面之后,现在可以开始创建登录页面了。首先,您需要用告诉用户登录的信息来更新大屏幕:
<section class="jumbotron">
<div class="container">
<h1>Login</h1>
<p>Login to your account</p>
</div>
</section>
接下来,您希望用您的登录表单替换当前的产品面板。因为这个站点是使用 Bootstrap 构建的,所以您可以简单地使用 Bootstrap 文档中指出的类( http://getbootstrap.com/css/#forms
)来实现表单。请注意,我已经将 id content
添加到了 section 元素中,稍后当您将内容拖入较大设备上的模式窗口时,将会用到它:
<section class="container" id="content">
<div class="row">
<div class="col-sm-12">
<form role="form">
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</section>
实现表单后,构建您打算为较小视窗实现的用户旅程,单击标题中的登录链接,用户将进入登录页面,如图 9-3 所示。
图 9-3。
The home page with navigation open (left), and the login page the user clicks through to (right)
下一步是研究如何通过将登录表单加载到较大视窗的模态窗口中来逐步增强站点。让我们使用 Twitter Bootstrap 附带的默认模态样式,但是您将添加 JavaScript 来在中型和大型视口中显示和隐藏模态。首先,将模态 HTML 添加到 index.html 文件中(这取自 http://getbootstrap.com/javascript/#modals
)。您会注意到,在代码中,我将 modal-body 留空,这是因为我将动态地从登录页面获取这些内容,这样在更新表单时,我只需更新 HTML 一次:
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h1 class="modal-title">Login</h1>
</div>
<div class="modal-body">
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
设置好模式窗口的 HTML 后,打开 main.js 文件并开始编写功能。让我们首先创建一个立即调用的函数表达式,它本质上是一个匿名函数,一旦被 JavaScript 引擎读取,就会被调用。在这种情况下,它允许您将变量的范围限定为代码库:
(function(){
}());
下一步是将模式窗口缓存在一个变量中,以便以后可以访问它。此时,您还将创建站点覆盖,它出现在模式窗口后面的一个变量中,以便您可以在显示模式窗口时将它添加到页面中:
var $modal = $('.modal');
var $modalBackdrop = $('<div class="modal-backdrop fade"></div>');
下一步是将激活时打开模式窗口的方法放在一起。首先,将该方法声明为一个名为loginClick
的变量:
var loginClick = function(e){
};
我前面提到过,login.html 页面上包含的表单将被拖到模式窗口中。为此,使用 jQuery 的ajax
方法,将 URL 传递给登录页面和一个包含成功方法的对象:
$.ajax('/login.html',{ success: function(data){
}});
检索登录页面的 HTML 后,过滤它以从页面中提取登录表单。为此,使用 jQuery 的filter
方法,传递前面包含登录表单的部分中指定的id
。过滤完返回的 HTML 后,使用html
方法从过滤后的数据中获取最终的 HTML,并将其输出到变量中:
var html = $(data).filter('#content').html();
过滤完 HTML 后,将其添加到模式窗口的主体中。因为已经在变量中缓存了模态,所以可以简单地使用 jQuery find
方法来查找模态体,然后使用html
方法从登录面板用 HTML 更新模态体内容:
$modal.find('.modal-body').html(html);
更新了模式体之后,现在可以显示模式窗口了。首先,使用 jQuery 的show
方法显示元素,然后使用removeClass
方法移除类out
(该元素可能在之前淡出内容时出现),最后使用addClass
方法添加in
类,这将导致元素使用 CSS3 过渡淡入:
$modal.show().removeClass('out').addClass('in');
接下来,您将通过使用insertAfter
将背景插入到模态窗口之后来显示背景,使用show
方法来显示背景,然后添加类in
,该类将执行 CSS 动画:
$modalBackdrop.insertAfter($modal).show().addClass('in');
将所有这些放在一起后,最终方法的代码如下所示:
var loginClick = function(e){
$.ajax('/login.html',{ success: function(data){
var html = $(data).filter('#content').html();
$modal.find('.modal-body').html(html);
$modal.show().removeClass('out').addClass('in');
$modalBackdrop.insertAfter($modal).show().addClass('in');
}});
e.preventDefault();
};
在这个阶段,您已经编写了显示模态窗口的方法,但是您不必编写任何 CSS,因为您只是使用了 Twitter Bootstrap 附带的样式。完成这些后,编写用于隐藏模态的方法。
首先,将方法定义为一个名为modalClose
的变量:
var modalClose = function(e){
};
接下来,删除使用removeClass
方法显示时添加到模态的in
类:
$modal.removeClass('in');
然后对背景做同样的事情:
$modalBackdrop.removeClass('in');
移除这些类将导致模态及其背景淡出。动画完成后,隐藏模态并移除背景,为此,您需要设置一个超时。Twitter Bootstrap 指定的动画是 300ms,我们就把超时设置为 300ms 后运行吧。在超时内,使用 jQuery hide
方法隐藏模态,然后使用remove
方法移除背景:
setTimeout(function(){
$modal.hide();
$modalBackdrop.remove();
}, 300);
最后,在modalClose
方法的末尾,使用事件的preventDefault
方法来阻止按钮触发默认的浏览器动作:
e.preventDefault();
将这个 close 方法组合在一起后,代码如下所示:
var modalClose = function(e){
$modal.removeClass('in');
$modalBackdrop.removeClass('in');
setTimeout(function(){
$modal.hide();
$modalBackdrop.removeClass('in').remove();
}, 300);
e.preventDefault();
};
到目前为止,我已经讨论了 open 和 close 方法,但是我还没有将它们附加到任何事件上。您只希望模式窗口显示在较大的视口中,因此只有在这些较大的视口中时才添加这些事件侦听器。为此,请使用 SimpleStateManager 创建一个状态,当您进入该状态时,将添加事件侦听器,如果您离开该状态,则将删除事件侦听器。
如前所述,要添加状态,请使用 SimpleStateManagers 的addState
方法。将状态的最小宽度设置为 768px,这样在特别小的设备上,用户可以直接进入新页面。对于onEnter
回调,使用 jQuery 的on
方法向标题中的登录链接和模式中的关闭按钮添加一个点击事件。在用户调整浏览器大小并离开状态的情况下,只需使用 jQuery 的off
方法来删除这些事件监听器:
ssm.addState({
id: "mediumUp",
minWidth: 768,
onEnter: function(){
$('.login-link').on('click', loginClick);
$modal.on('click','.close', modalClose);
},
onLeave: function(){
$('.login-link, .modal').off(‘click’);
}
});
添加了状态之后,现在只需运行 SimpleStateManagers 的ready
方法来告诉库设置状态:
ssm.ready();
准备就绪后,在具有较大视窗的浏览器中打开页面,然后单击 login 链接。这将显示灯箱,如图 9-4 所示。
图 9-4。
The modal window shown on larger viewports
完成这个例子之后,您现在已经实现了第一个响应式 JavaScript。正如您所看到的,根据用户的响应状态为他们提供不同的功能并不困难,但是,它确实让用户在使用网站时有更好的总体体验。因此,我们的目标应该是找出网站中在特定设备上不太好用的地方,然后利用这些技术来优化它们。
摘要
已经分别研究了功能检测和状态管理,然后研究了如何一起使用它们,您现在应该很好地理解了如何使用 JavaScript 通过更改站点的功能以最适合用户的设备来改善用户体验。
你应该从中吸取一些要点:
Providing the same functionality to all users, regardless of device, can cause a bad user experience. Functionality can be targeted based on both the state of the browser and the features it supports. You can use feature detection alongside the state of the browser to enable you to progressively enhance your site.
我们已经了解了如何响应设备,以及如何根据视窗大小和设备支持的特性来改变功能,下一章将更深入地探讨在构建一个响应式站点时必须做出的用户体验决策。
Footnotes 1
http://christianheilmann.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/
。
十、优化您的响应网站
不管你是已经建立了你的网站,还是即将开始建立一个响应性的项目,你都需要花时间来优化你的网站。性能不是一个很好的特性,而是多设备现代网络的必需品,因为它是用户体验的一个关键部分。然而,网站性能是一个非常大的主题领域。事实上,有些书只讲述了其中的一部分,所以在这一章中,我将把重点放在网站性能和网站优化方面,这些方面可以给一个响应性网站带来最大的不同。
在过去的几年中,家庭和办公室的互联网连接速度显著提高,利用可用带宽的增加,使 Web 内容变得更加丰富、互动和多样化。问题是,这导致了网站的平均规模不断膨胀,尤其是图片和 JavaScript 的权重不断增加。
在同一时期,用户一直在改变他们的浏览习惯,转向移动设备,这些设备充其量只能连接 4G 网络,但更有可能的是,他们将使用较慢的 3G 网络,有时甚至是 2G 网络。这意味着我们比以往任何时候都需要寻找方法来精简我们的网站,同时为用户提供最好的体验。
这一章将探讨优化网站性能的方法。具体来说,您将了解:
Improving network performance The critical rendering path Server-side optimization techniques Measuring your site performance
为什么您应该关注站点性能
在过去几年中,网站性能已经成为一个大问题,网页越来越大,HTTP Archive 的统计数据表明,2013 年 3 月平均网页为 1,311KB,2014 年 3 月平均网页为 1,703KB。 1 这比一年的时间增加了 392KB。
如果我们进一步研究 2014 年 3 月平均网页的资产细分(摘自 HTTP Archive),您会发现各种显示形式的大小如下:
Images: 1,063KB Scripts: 276KB Other: 207KB HTML: 56K Style sheets: 48KB
虽然平均网页的大小在增加,但用户对加载时间的期望却在减少,今天的 Web Performance 强调,用户在放弃之前等待站点加载的时间每年都在减少。另一篇文章强调了这样一个事实:较慢的网页会导致用户较少参与网站,这意味着它会降低网站的效率。 3
有时很难证明业务对性能的需求,因为优化一个站点需要时间成本。然而,网页表现和将用户转化为所提供产品的销售是有关系的。2014 年 4 月,Web Performance Today 发布了一篇关于这种关系的文章,强调沃尔玛的网站发现,加载时间每改善一秒,转化率就增加 2%。同样,这篇文章强调了 Mozilla 的改进,他们将加载时间减少了 2.2 秒,导致下载量增加了 15.4%,相当于每年增加 1000 万次下载。这种关系凸显了企业对更好的 web 性能的需求。
提高网络性能
网络会对用户加载网站的速度产生重大影响。网络对网站性能的影响可以分为两个方面:带宽和延迟。
带宽是数据在互联网上从 A 点传输到 B 点的速度,通常是 ISP 向最终用户出售互联网套餐的衡量标准。通常带宽是以每秒多少比特来衡量的,所以一个互联网包通常以 X Mbs 出售。带宽主要通过允许用户下载网站资产的速度来影响网站用户。
延迟是数据包发送到服务器到发送方收到数据包之间的往返时间。然而,提高带宽与改善延迟没有直接关系。这是显而易见的,尽管带宽持续快速提高,但延迟并没有得到同样的改善。伊利亚·格里戈利克在他的博客文章中进一步阐述了延迟如何成为新的网络性能瓶颈。 5
伊利亚·格里戈利克在他的书《高性能浏览器联网》中讨论了用户接入互联网之前,不同类型的移动网络之间的延迟,特别是他指出,在 3G 连接上,用户通常有 100 到 500 毫秒的延迟。在 4G 连接中,这个时间会提高到大约 100 毫秒,然而,Ilya 强调,由于标准的演进方式,3G 和 4G 之间的界限变得模糊。这是因为 4G 本身并不是一项技术,它实际上是一项技术要成为 4G 所必须满足的一系列要求。这导致了两种不同的技术正在开发中——LTE 和 HSPA+—每种技术都互不相同,这意味着预计延迟和带宽也会有所不同。
Natasha Rooney 在 2014 年 2 月的伦敦网络标准会议上谈到了移动电话网络如何影响网站,特别是她谈到了它们如何增加网络连接的延迟。
增加延迟的原因是在移动电话和互联网之间存在移动电话网络(如图 10-1 所示),这在沿途的几个点上引入了延迟。
图 10-1。
Illustration explaining how a mobile phone connects to Internet
在谈到延迟时,Natasha 提到了相关的不同延迟:
Control plane latency (approximately 100ms latency): To establish the radio connection, a one-time latency is introduced due to the process of the device’s radio transitioning from being in a standby state to an active state. User plane latency (approximately 5ms latency): The latency incurred by each packet of data being transferred between the device and the carrier’s radio tower. Core network latency (approximately 30-100ms): The latency caused by transferring packets between the radio tower to the carrier gateway, which is variable dependent on the mobile phone network. Internet routing latency (variable latency): The latency between the carriers gateway and the requested destination address.
这些延迟对加载网站有负面影响,站点发出的每个额外的 HTTP 请求都会导致额外的延迟。这就是为什么如果你研究提高网络性能的技术,你会注意到有一个重复出现的主题;目的是减少 HTTP 请求的数量,并减少传输给用户的数据。
现在,了解了开发人员在网络性能方面面临的问题后,让我们来了解一下可以用来提高网络性能的不同技术。
连接文件
网络性能的一个问题是,当您试图加载大量文件时,会增加网站的开销。这是因为网站需要执行大量的 HTTP 请求。为了解决这个问题,您需要确定如何减少 HTTP 请求的数量,为此,您需要减少包含在网站中的文件数量。
减少文件数量的一种方法是将它们连接起来,我的意思是将它们合并在一起。一个例子是将几个样式表按照它们在页面上被引用的顺序合并成一个文件。
手动连接文件可能是一项耗时的任务,然而,在第七章中,我解释了工具 Grunt 的使用,它可以用来自动完成这项任务。特别是,您可以使用 grunt-contrib-concat 任务来连接您的文件。
优化图像
在大多数网站上,图片占据了页面的大部分权重,因此优化图片非常重要。有许多方法可以优化网站上使用的图像,通过结合以下技术,可以减轻页面重量,减少用户加载网站时的延迟。
鬼怪;雪碧
当使用 CSS 中的图像作为背景时,最终可能会有大量的图像,加载每一个图像都会增加额外的 HTTP 请求。
减少用于加载背景图像的 HTTP 请求数量的一种方法是使用精灵。在这里,您可以将多个图像合并成一个图像,然后使用背景定位来选择您想要使用的图像部分。
要创建精灵,只需将图像资源合并到一个主图像中。这可以通过 Photoshop 之类的图形包或者使用 SpritePad 之类的 sprite 生成工具来实现(wear ekiss . com/sprite pad
)。一般来说,你不需要在图像之间留任何空间,这样得到的精灵看起来应该如图 10-2 所示。
图 10-2。
An example of a sprite showing four icons
组装好 sprite 工作表后,现在需要编写 CSS 来将它显示给用户。这是通过设置一个共享的背景图像,然后根据您想要显示的图像简单地更改背景位置来实现的。生成的 CSS 如下所示:
.orange-smile-icon,
.green-smile-icon,
.blue-smile-icon,
.pink-smile-icon {
width: 25px;
height: 25px;
background: url('sprite.png') 0 0 no-repeat;
}
.orange-smile-icon{
background-position: 0 0;
}
.green-smile-icon{
background-position: -25px 0;
}
.blue-smile-icon{
background-position: -50px 0;
}
.pink-smile-icon{
background-position: -75px 0;
}
除了 HTTP 请求的数量之外,sprite 还可以为您的资产大小节省一点空间,例如,您刚刚使用的 sprite 只有 2.6KB,而单个图像的组合文件大小为 5.6KB。节省空间的原因是文件必须包含描述符来定义文件格式,因此,因为您只有一个文件,而不是四个文件,所以只有一组描述符增加了您的文件大小。
在第七章中,我讨论了 CSS 预处理器,其中一个是 Sass,它可以用一个叫做 Compass 的框架来扩展。Compass 最受欢迎的特性之一是,它允许您从图像文件夹中生成一个 CSS sprite 以及相应的代码。这样做的好处是,您不必手动创建 sprite 工作表,添加新的 sprite 就像将新图像放入包含该图像的文件夹中一样简单。
内嵌图像
数据 URI(统一资源标识符)是一种可以将数据内联到网站文件中的方法,与包含外部资源的方法类似。通过选择在 HTML 或 CSS 中包含资产的数据,而不是执行额外的请求来获取外部文件,它们使您能够减少 HTTP 请求的数量。
作为一个简单的例子,你可以使用位于 http://jpillora.com/base64-encoder/
的 Base64 文件编码器。你将编码一个简单的笑脸(如图 10-3 所示),大小为 25px ×
25px。
图 10-3。
Individual icon before Base64 encoding
通过将其从图像转换为 Base64 编码的图像,然后可以将其用作数据 URI,最终结果如下:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5M0RFQzE3RUJDRUUxMUUzQUQyNkY0RUE1MjU3NzRBNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo5M0RFQzE3RkJDRUUxMUUzQUQyNkY0RUE1MjU3NzRBNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjkzREVDMTdDQkNFRTExRTNBRDI2RjRFQTUyNTc3NEE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjkzREVDMTdEQkNFRTExRTNBRDI2RjRFQTUyNTc3NEE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+teXwJgAAARpQTFRF//z5/4wv/3wS//37/34W/3sQ/3sR/+va/7qC/9i4/3oP/72I/6dg//z6/34V/4su/44y/6JW/+nX/9Cq/38Y//v3/8CN/6xp/4os/+LL/9Ku//v4/8KR/40x//r2/+HJ/7+L//fx/+nY/+zc/6tn/7Jz/8KQ/9/F/8ib/7Bx/8md/4Mf/3gK/6lj/6xo/5ZC//jy/5xM/3QE/9Gr/6df/9u+/5I5/5tL/5Q+/4Eb/3oO/+LK/9a1//n0/3UG/+vb/+3f/72H/6lk/30T/4AZ/8ic//38/3YH/5hF//jz/9q8/5Q9/7yF/4cm/38X/5M7//n1/6Vd/8GP/9e3/+3e/40w/55Q/76J/+PM//Hl/8qf/5ZB/3IAhKDRywAAARNJREFUeNpiiIEABk5xRqZYJkY9TgaYCJhkdrWMhQEWMV6EjL1sLDIIVYLJSDrHogI2eYiMMl8sOmBSAskwCwCZHIocUEEORR8gycgLlFED8RliPKEycjG2IEohhoFBBMTgDuCGyjjKhYNdKMogFYsdCDM44ZDhYWDEISPBwIZDhovBCIdMLB49jLHmghjCdjpAe3Rj/SNDAv2Qxd15TAWBbgP6JxoYDfJaqmEeFt76hioyXtIx3CD/gMLAzCAGBaiCwyAmCKjfSsYNIW7jCwk3SFjHcplEqbM7KEcYa2qDuPygsI5hx4wfViFonDKhR5w1LB2wo6YDfiGktKPhgpR2GJBSFSS9scayMqoES0NFAAIMACNkvRK6zkxYAAAAAElFTkSuQmCC
您需要知道,使用 Base64 编码的图像比图像文件本身占用的空间更大。在上面的例子中,原始图像是 1.4KB,但是,Base64 编码的图像实际上是 1.95KB,增加了大约 39%。因此,虽然您减少了 HTTP 请求的数量,但是也增加了文件的大小。
除了增加图像的文件大小,您还需要考虑增加样式表的文件大小。这导致下载时间的增加,进而导致页面呈现的更大延迟。考虑到这一点,您需要考虑哪里适合使用这种技术,哪里不适合。
最适合将图像作为样式表的一部分的例子是用于导航的图标。这种图像相对来说比较小,所以不会超出样式表,而且因为它们在站点中使用,所以将它们作为样式表的一部分预加载可以避免额外的 HTTP 请求。要最小化添加到样式表中的大小,您可以使用 Base64 对图标的单个 sprite 进行编码,然后像使用 sprite 一样使用该图像。
使用正确的图像格式
开发网站时,考虑网站中不同图像所使用的文件格式是很重要的。目前网络上使用的图像文件格式主要有四种:JPEG、PNG、GIF 和 SVG。
GIF 格式
GIF 格式是一种 8 位图像格式,支持 256 种颜色的调色板。
在构建网站时,GIF 格式最适合于颜色相对较少的图像,如徽标、简单图形和图表。
最近,GIF 格式的使用由于其对动画的支持而复兴,这导致它被用于在 Web 上显示类似视频的内容。
联合图像专家组
JPEG 格式是由联合图像专家组开发的 24 位有损格式,是网络上最流行的图像文件格式。这种流行的原因是 JPEG 实现的压缩非常好,并且这种格式允许您在文件大小和质量之间选择您想要的平衡。
使用 JPEG 格式实现的压缩级别是由于它丢弃了被认为不必要的图像数据。保存图像的人可以选择不同的压缩级别,但是,对图像应用的压缩越多,格式丢弃的数据就越多,导致图像质量下降。如果您想要保持良好的质量压缩比,您通常会希望将图像的压缩级别设置为 70%或 75%左右。
在构建网站时,最有可能找到 JPEG 格式的地方是照片,因为它们通常有很多细节,而其他格式可能会导致一个大文件。
由于压缩图像的方式,JPEG 格式不适合只有几种颜色的图像(如徽标)。这是因为使用 JPEG 格式会降低质量,并可能引入一些伪像,这在只有几种颜色的图像上是显而易见的。在这种情况下,您应该考虑使用其他图像格式。此外,JPEG 格式不支持透明度,因此如果您需要透明度,您将需要使用不同的文件格式,如 PNG。
巴布亚新几内亚
PNG 格式是一种无损格式,有两种不同的风格:PNG-8 和 PNG-24。PNG-8 支持 256 种不同的颜色,而 PNG-24 格式支持 1600 万种颜色。该格式还支持透明性。
当考虑 PNG 在哪里最合适时,这取决于 PNG 格式的味道。PNG-8 风格适用于颜色相对较少的图像,如徽标、简单图形和图表。在大多数情况下,PNG-8 文件比 GIF 文件小。
然而,PNG-24 的味道非常不同,可以用于多种用途。它支持完整的 alpha 透明度,因此您可以将透明度作为详细图像的一部分。无损格式意味着 PNG-24 文件可能会非常大,所以不要过度使用这种格式很重要,因为它会对网站的文件大小产生负面影响。
挽救(saving 的简写)
SVG 是可缩放矢量图形的缩写,是一种基于 XML 的矢量图像格式,可以在网站上使用。它最适合图标和徽标等颜色数量有限的图像。
作为矢量格式,图像可以在不损失任何图像质量的情况下进行缩放。对于响应式设计,这是理想的,因为这意味着图像在各种不同的设备上看起来都很清晰,包括那些高像素密度的设备。
遗憾的是,SVG 图像格式在 Internet Explorer 8 和更低版本中无法工作。在这些较旧的浏览器中支持 SVG 的最简单的方法是使用一些基本的 JavaScript 简单地使图像返回到 PNG。在第九章我讨论了 Modernizr.js,它可以让你检测浏览器特性,其中一个可以检测的特性就是 SVG。
让我们看一个例子,使用 Modernizr.js 进行特性检测,并使用 jQuery 处理一些快速 DOM 操作,将这种回退处理为 PNG。首先,您需要编写 HTML。让我们在一个名为data-png
的数据属性中引用图像的 PNG 版本的 URL。您还必须添加一个名为svg
的类来突出显示这是一个您想要用 PNG 更新的图像。使用以下代码来实现这一点:
<img id="logo" src="logo.svg" data-png="logo.png" width="50" height="50" alt="Logo" />
有了 HTML,现在可以编写 JavaScript 来处理将 SVG 替换为 PNG。先用Modernizr
检查浏览器是否支持 SVG 如果不是,那么使用 jQuery 遍历添加了.svg
的每个元素,然后用存储在data-png
中的值替换当前的src
属性:
if(!Modernizr.svg){
$('.svg').each(function(){
var $this = $(this);
$this.attr('src', $this.attr('data-png'));
});
}
图像压缩
考虑好要使用的图像格式后,尽量减小图像的文件大小是很重要的。像 Adobe Photoshop 和 GIMP 这样的图形软件包通常不会以最佳方式保存图像,但是,有一些第三方工具可以最大限度地压缩图像,因此您可以最小化文件大小。目的是在不降低图像质量的情况下减小图像的文件大小。
有许多工具可以实现图像压缩;让我们探索一下这些不同的工具。
Smush.it
Smush .它是由 Yahoo!它能够对各种不同的图像文件类型执行压缩。
这个工具有几种不同的用法,第一种是把你的图片上传到网站上( http://www.smushit.com/ysmush.it/
)
)。上传图片后,网站会对其进行处理,然后允许你下载压缩版本。
不幸的是,将所有图片手动上传到网站非常耗时,所以另一个选择是使用命令行工具。这个命令行工具是一个节点工具,所以你需要确保你已经安装了节点(关于它的教程可以在第七章中找到)。要安装该工具,您应该使用终端命令:
npm install node-smushit -g
安装后,您可以使用以下命令压缩单个映像:
smushit imagename.png
虽然压缩单个图像很容易,但通常您可能想要压缩整个图像文件夹,而不是单个图像。为此,您可以向 smushit 命令传递文件夹而不是图像名称:
smushit img/
如果使用 Grunt,可以通过使用 grunt-smushit 任务,使用 Smush.it 自动压缩图像。正如第七章中所讨论的,这与其他繁重任务的安装方式相同。
无损压缩
另外一个图像优化的工具是 ImageOptim,可以从 http://imageoptim.com
下载。要使用该工具,打开应用程序,您将看到如图 10-4 所示的窗口。
图 10-4。
The ImageOptim tool, drag your images onto the window to compress them
要压缩图像,只需将想要压缩的图像拖到窗口的中央。将图像放入该工具后,它将开始压缩您的图像。当它完成一个图像,一个小勾会出现在左边的行和信息提供了多少压缩节省。如图 10-5 所示。
图 10-5。
ImageOptim showing a list of images along with the savings made by compressing
目标图像响应
在前面讨论网络时,我讨论了在任何给定时刻可用的带宽如何影响网站下载站点资产的速度。我还注意到,由于小型设备使用的连接类型,它们的可用带宽通常低于大型设备。
在响应式站点上工作时,您可能会以不同的大小显示图像,大小取决于设备视窗宽度的大小。虽然浏览器可以简单地根据视窗的大小缩放图像,但是更好的选择是加载特定于该视窗大小的图像。由于图像在网站的文件大小中占很大比例,因此对于这些较小的设备来说,确保您交付的图像尽可能小是有意义的,这些设备通常会在延迟增加而带宽减少的连接上运行。虽然使用正确的文件类型和图像压缩会有所帮助,但是您可以通过在这些较小的设备上使用较小的图像来实现最大的节省。
有两种方法可以实现响应式图像,这两种方法都是“图片”元素规范的一部分(该规范是 HTML 生活标准的一部分,可以在 http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html
)
找到)。
img 元素
实现响应式图像的一种方法是通过在img
HTML 元素上使用新的srcset
属性,这允许您定义多个图像,以及何时应该使用每个图像的提示。
我们要看的srcset
的第一个用途是根据设备的像素密度来定位图像,这里的主要好处是您可以为具有高像素密度屏幕的设备提供更高分辨率的图像,这将使它们看起来更清晰。为了实现这一点,我们需要使用属性“srcset”,将值设置为逗号分隔的图像列表,每个图像都有图像的像素密度。我已经在图 10-6 中展示了这一点。
图 10-6。
The logic required for targeting images based on pixel density
了解了使用 srcset 属性的逻辑之后,我们现在来看一个示例,看看它在我们的代码中是什么样子的:
<img srcset="low-res-image.png 1x, high-res-image.png 2x">
值得注意的是,在这个例子中,我还包含了一个src
属性,这允许不支持srcset
的传统浏览器退回到较低质量的图像,而不是无法显示图像。我已经在这里展示过了:
<img srcset="low-res-image.png 1x, high-res-image.png 2x" src="low-res-image.png">
使用srcset
属性的一个更好的方法是根据视窗的宽度来定位图像。实现这一点的第一步是设置srcset
属性的值;为此,使用逗号分隔的图像,每个图像都有定义的源图像宽度(参见图 10-7 )。
图 10-7。
“srcset” being used to define the widths of our images
这就是事情开始变得有点复杂的地方,尽管你已经告诉浏览器每张图片有多大,但你还没有告诉浏览器在任何给定点图片有多大。这就是sizes
属性的用武之地。使用sizes
属性,您可以告诉浏览器,对于任何给定的视窗大小,您期望图像的大小是多少。你可以使用我们在第三章中作为媒体查询的一部分使用的相同媒体表达来实现这一点。对于每个媒体表达式,我们还指定当媒体表达式为真/匹配时图像的宽度。如果您指定一个不包含媒体表达式的宽度值,当没有媒体查询匹配时,该宽度将作为图像的默认宽度(参见图 10-8 )。
图 10-8。
“sizes” attribute being used to define the widths the image appears at each breakpoint
你会注意到,在上面的例子中,我用vw
单位指定了宽度,用视窗宽度的百分比表示。因此,当您指定50vw
时,您是在指示 50%的视口宽度。如果图像总是浏览器宽度的 100 %,您可以只设置默认值为100vw
,而不指定任何其他规则。sizes
属性并不限制您使用vw
宽度单位,您可以选择使用其他宽度单位,如 em 或 px。
设置了srcset
和sizes
属性后,您的浏览器就能够自动确定最适合加载的图像。在这样做时,浏览器可以考虑设备的许多特性,这些特性包括显示器的像素密度、图像相对于视口宽度的大小、用户偏好(用户可以选择加载较低质量的图像以减少数据消耗)以及用户连接的网络类型。一个完成的img
元素实现了带有图像宽度的srcset
属性,如下所示:
<img srcset="low-res.png 500w, high-res.png 1000w" sizes="(min-width:480px) 50vw, 100vw" >
需要注意的是,不能将基于宽度的srcset
和基于像素密度的srcset
混合使用,因此,虽然知道基于像素密度的srcset
很重要,但最好使用基于宽度的srcset. This is
,因为,正如我们已经提到的,它给了浏览器选择最合适图像的控制权。
作为一个新属性,浏览器对srcset
的本地支持是有限的,但是,因为它增加了现有的src
属性,所以您仍然可以使用它来为浏览器加载默认图像。然而,如果你想在所有浏览器中完全使用srcset
,你可以选择使用多填充。Scott Jehl 的 PictureFill 就是这样一个聚合填充,可以在 https://github.com/scottjehl/picturefill
找到。PictureFill 旨在填充图片元素的完整规范,包括将srcset
添加到img
元素中。
图像来源
定义响应图像的另一种方法是使用新的picture
元素。picture
元素被开发为一种方式,允许开发人员基于许多特征(包括格式、分辨率和方向)交付适合设备的图像。这样做的目的是,浏览器可以根据其当前的环境选择最佳版本的图像进行加载。
如果我们现在查看组成图片元素的每个组件,首先要查看的是图片标签。这包含了构成我们图片元素的所有部分。
<picture>
</picture>
在图片元素内部,我们有多个“源”元素。这些“源”元素中的每一个都应该有一个用于媒体表达式的“媒体”属性。当确定应该显示哪个图像时,浏览器将遍历每个“源”元素,直到它找到具有匹配/正确的媒体表达的图像。除此之外,我们还需要包含一个“srcset”属性来指定要使用的图像。类似于我们已经在 img 元素上使用的“srcset ”,我们可以用它来指定多个图像。在下面的例子中,我们为每个“源”元素指定了一个单独的图像。
<picture>
<source srcset="large.jpg" media="(min-width: 980px)">
<source srcset="medium.jpg" media="(min-width: 768px)">
</picture>
在我们的源元素之后,我们还需要包含一个img
元素。当使用img
元素作为picture
元素的一部分时,我们可以使用srcset
属性来指定默认图像。此外,我们需要在img
元素上为我们的图像指定 alt 文本。我们还应该为不支持picture
元素的浏览器包含一个使用src
属性的默认图像。
<picture>
<source srcset="large.jpg" media="(min-width: 980px)">
<source srcset="medium.jpg" media="(min-width: 768px)">
<img src="small.jpg" srcset="small.jpg" alt="A responsive image">
</picture>
看完了picture
元素的不同部分,我们现在来看一个如何使用它的例子。在下面的例子中,我们定义了三个图像,前两个作为source
元素,第三个作为img
元素。对于每个源元素,我们使用了媒体属性来应用媒体表达式。
<picture>
<source srcset="large.jpg" media="(min-width: 1200px)">
<source srcset="medium.jpg" media="(min-width: 600px)">
<img src="small.jpg" srcset="small.jpg" alt="The church where I got married">
</picture>
如果我们在浏览器中加载一个小视窗(在本例中小于 600 像素宽)的页面,然后打开开发工具,我们会看到浏览器只下载较小的图像,如图 10-9 所示。
图 10-9。
Our small image being loaded on the small viewport size
通过只下载一个较小的图像到较小的设备上,我们减少了页面的文件大小,这有助于提高我们网站在较小设备上的性能。虽然这可以通过使用scrset,
来实现,但是picture
元素增加的冗长性允许我们选择在哪个设备上显示哪个图像,而不是让浏览器来选择。这使我们能够根据设备的大小灵活地改变图像的艺术方向。看了在较小的视窗中加载我们的站点时会发生什么,我们现在将在一个中等大小的视窗中加载我们的页面,如图 10-10 所示。
图 10-10。
Our page loaded with a medium sized viewport, the medium sized image is therefore loaded
不出所料,浏览器选择下载我们指定为源元素之一的中等大小的图像。
浏览器对picture
元素的支持很好,因为该规范旨在允许不支持它的浏览器简单地使用包含的图像元素。此外,如果我们希望对图片元素启用完全支持,而不仅仅是这种回退,我们可以使用我们已经提到的 PictureFill polyfill。
这方面的例外是 Internet Explorer 9,不幸的是,它与我们在图片元素中包含的source
元素有一个问题,因为它会将它们从页面中删除。为了防止这种情况,我们可以使用一种黑客技术,将source
元素包装在一个视频元素中。使用条件注释将功能定位于 Internet Explorer 9,然后我们可以将视频元素设置为不可见。这将阻止 Internet Explorer 9 删除source
元素,因此 polyfill 将能够使用它们。为了说明我们如何做到这一点,在下面的例子中,我们在源元素之前的开始视频元素标签周围和结束视频元素标签周围使用了条件注释。
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="large.jpg" media="(min-width: 1200px)">
<source srcset="medium.jpg" media="(min-width: 600px)">
<!--[if IE 9]></video><![endif]-->
<img srcset="small.jpg" alt="The church where I got married">
</picture>
因为可以同时使用srcset
属性和picture
元素,所以这不是选择是使用picture
元素还是使用srcset
属性的问题。相反,这是一个为你的具体情况选择你需要的详细程度的问题。
如果你只是以不同的分辨率提供相同的图像,这样你的网站就能提供最佳的图像,允许浏览器选择最适合浏览器环境的图像,那么使用srcset
和sizes
属性会很好。但是,如果您想将不同的图像指向特定的视窗尺寸,并完全控制图像显示的视窗尺寸,那么您应该选择使用picture
元素将图像指定为source
元素。
我们也可以选择使用srcset
和sizes
属性为每个source
元素指定不同的图像。为此,我们可以在我们的source
元素上使用媒体表达式来控制艺术方向,然后指定多个图像作为srcset
的一部分,同时定义sizes
以使浏览器能够选择要显示的图像的最佳版本。
有条件地加载内容
对于一个响应式网站,通常你已经将所有内容预加载到页面中;但是,有时您可能希望根据视口的大小有条件地加载内容。当建立一个网站移动第一,你把重点放在页面上的核心内容。然而,当网站首先在一个更大的设备上被浏览时,你就有了所有这些额外的空间,你可以很好地利用它们。您可以实际测试视窗是否足够大,然后使用 AJAX 动态加载附加内容,而不是将内容添加到 HTML 中并隐藏它(这只会增加页面权重)。
有条件地加载内容的好处是,通过从初始页面加载中去掉并非在所有设备上都可以看到的内容,可以降低初始页面权重。此附加内容中包含的任何资产也不会被下载。
然而,主要的缺点是您使用了额外的 HTTP 请求,这意味着用户必须等待加载额外的内容,所以如果内容真的很重要,您可能不希望采用这种方法,以便用户能够立即看到它。
域分片
浏览器中 HTTP 的当前实现限制了可以同时处理的 HTTP 请求的数量,并且这个数字在两个到八个连接之间变化。相比之下,对网站主域名的 HTTP 请求平均为 51 次(2014 年 3 月,HTTP 存档)。这意味着许多请求将等待其他请求完成,这会延迟网站的加载。
要克服这一限制,您可以使用一种称为域分片的技术,在这种技术中,您可以跨一系列域或子域部署页面的资源,以使浏览器能够同时请求更多的文件。这使您能够克服当前 HTTP 实现所强加的八个 HTTP 请求的限制。
通过跨一系列域加载资产,可以克服八个 HTTP 请求的限制,理论上可以更快地加载资产。这里的问题是这样做的好处可能不同,因为域分片实际上是现代浏览器的反模式。我的意思是,域分片是克服当前浏览器的 HTTP 实现所带来的限制的常见解决方案。然而,虽然在某些情况下它可能会工作,但在其他情况下它可能会降低性能。性能下降的原因是浏览器需要为每个使用的域进行额外的 DNS 请求,因此如果 DNS 响应缓慢,内容实际上可能需要更长的时间来下载。
Mobify ( http://www.mobify.com/blog/domain-sharding-bad-news-mobile-performance/
)做了一些研究,看看在我们现在生活的这个多设备的世界里,域名分片是否真的有益。他们的研究侧重于在移动浏览器中进行测试,并发现在所有测试的浏览器中,域分片几乎没有什么好处,事实上,在一些测试中,它实际上导致页面加载资产的时间增加。
服务器配置
服务器的配置方式会对最终用户的网站性能产生显著的影响,因此,尽可能地对其进行优化是有意义的。
启用服务器端压缩
要提高服务器性能,您可以做的第一件事是为基于文本的资产启用服务器端压缩。
当用户的 web 浏览器向 web 服务器请求页面时,浏览器将发送一个标题,让服务器知道它支持压缩内容。它还让服务器知道支持哪种类型的压缩,最常见的两种压缩类型是 GZIP 和 deflate。
因为服务器知道用户的浏览器支持压缩,所以在发送每个请求的文件之前,它将压缩该文件,然后将压缩的版本发送到用户的浏览器。在接收到文件时,浏览器查看文件的标题,如果是压缩文件,它将向浏览器指示该文件是压缩的,以及使用了哪种压缩。浏览器可以解压缩,然后处理该文件。
在 Apache 中启用压缩可以通过.htaccess
文件来实现,并且您可以基于您的站点提供的文件类型来设置规则。有两种方法可以使用.htaccess
文件启用压缩,第一种是向文件类型添加输出过滤器:
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/javascript
启用压缩的第二种方法是对具有特定扩展名的所有文件进行压缩:
<files *.html>
SetOutputFilter DEFLATE
</files>
使用过期标题
当网站的用户第一次访问时,他们的浏览器需要下载页面的所有资源,包括 HTML、图像、CSS 和 JavaScript。如果没有浏览器缓存,每次用户访问网站时,用户的浏览器都必须这样做。
不幸的是,web 浏览器根本不知道这些文件是否已经更改,所以这就是为什么每次用户访问站点时都需要重新加载它们。作为开发人员,我们需要通知浏览器哪些文件不太可能经常更改,以便浏览器能够有效地缓存它们,这可以通过使用 Expires 头来实现。
Expires 头告诉浏览器它接收的文件在一定时间后才会改变。这对浏览器的好处是,它能够缓存文件,因为它知道在规定的时间过去之前,不必再次重新提取文件。当您的用户再次访问该站点时,浏览器只是从缓存中加载文件,而不是获取文件。
在配置 Expires 头之前,考虑您希望用户的浏览器缓存什么类型的文件是很重要的,以下是您可能希望缓存的一些建议:
CSS JavaScript Images
还有一些不希望使用过期标头的示例:
AJAX APIs Dynamic pages
在 Apache 中设置 Expires 头可以通过将它们添加到 sites .htaccess
文件中来实现。第一步是启用 Expires 标头并设置默认规则,该规则将被添加到网站提供的所有文件中,在下面的示例中,我设置了默认规则 1 周:
<IfModule mod_expires.c>
ExpiresActive On
# Add a default rule
ExpiresDefault "access plus 1 week"
</IfModule>
启用 Expires 头之后,您可以开始为 CSS、JavaScript 和图像定义自定义规则。您可以通过使用ExpiresByType
来做到这一点,包括文件类型和您希望文件在用户缓存中保留的时间长度。
<IfModule mod_expires.c>
ExpiresActive On
# Add a default rule
ExpiresDefault "access plus 1 week"
# CSS rules
ExpiresByType text/css "access 1 year"
# Javascript rules
ExpiresByType application/javascript "access plus 1 year"
# Images rules
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
</IfModule>
CDN 上的主机
在决定如何提高网络性能时,一个选择是考虑使用内容交付网络(CDN)来为您的站点资产提供服务。这样做可以提高性能的原因是,用户与 web 服务器的距离会影响响应时间。CDN 是分布在多个地理位置的分布式服务器网络,这意味着您的用户能够通过离他们最近的服务器查看您的网站。
除了更接近你的用户,使用 CDN 还可以帮助你提高网站的可用性。由于分布在多个位置,如果一个位置出现连接问题,您的用户可以继续由其他位置提供服务。
有许多可用的 CDN 提供商,一些流行的提供商包括:
Akamai Amazon Web Services CloudFront Cloudflare
SPDY/HTTP 2.0
SPDY 和 HTTP 2.0 都是过时的 HTTP 协议的替代品,在性能和功能上都优于其前身。
在提到的网络性能方面,延迟是现有 HTTP 协议的一个弱点。这是因为 HTTP 是为一种与我们今天构建的网页类型非常不同的网页而设计的,并且 web 发展的方式在最初开发 HTTP 协议时是无法预料的。
Chromium SPDY 白皮书 7 强调了现有 HTTP 协议面临的一些问题:
Single request per connection Only clients can initiate a request HTTP headers are uncompressed Redundant HTTP headers
为了克服这些问题,Google 开发了 SPDY 协议。他们在白皮书中列出的目标是:
Allow concurrent requests across a single TCP connection Reduce the bandwidth by compressing headers and eliminating unnecessary headers Reduce complexity that we get when using HTTP Enforce SSL as standard by using it as the underlying transport protocol Allow the server to initiate connections to the user’s browser
要开始使用 SPDY,您需要确保您的服务器设置为支持该协议。如果你使用 Apache,你需要安装mod_spdy
模块,如果你使用 nginx,你需要安装http_spdy_module
模块。有了这个设置,您还需要购买一个 SSL 证书,并在您的域上设置它。最后,您将希望重定向所有流量以使用您站点的 HTTPS 版本,以便所有用户看到站点的相同版本并共享相同的链接,并且那些拥有支持 SPDY 的浏览器的用户将开始看到性能优势。
SPDY 为需要加载大量资产的站点提供了最大的好处。这是因为它允许网站的文件并行下载,而不是批量下载,并且由于这种并行下载是通过单个 TCP 连接进行的,因此也减少了数据包丢失。
除了这些好处,SPDY 还允许服务器在用户没有请求的情况下将内容推送到用户的浏览器。这里的想法是,在页面完成下载之前,服务器可以发送相关的文件,比如图像、样式表和 JavaScript 文件。
SPDY 正在所有主流浏览器中实现,最近在 2014 年全球开发者大会上宣布支持 Safari。这意味着很可能大量的用户将会使用或很快会使用支持 SPDY 的浏览器。因此,您需要确保托管您的站点的服务器设置为支持 SPDY。
在了解了 SPDY 是什么以及它所提供的新的性能优势之后,它为 HTTP 2.0 协议最终确定后的预期提供了一个良好的基础。HTTP 2.0 协议目前正在开发中,它基于 Google 开发的 SPDY 协议。
关键渲染路径
加载网页性能的一个重要概念是关键呈现路径。Google 将关键渲染路径定义为“渲染网页初始视图所需的代码和资源” 8
为了呈现网页,用户的浏览器必须从它们所在的 web 服务器上下载它所使用的资源。关键路径是在向用户显示任何内容之前必须下载和处理的文件,因此,提高关键呈现路径的性能有助于提高用户对站点性能的感知。
让我们看一个简单的例子,看看关键的渲染路径是如何在浏览器中渲染的。让我们从一些典型的 HTML 开始,它包含一个样式表、一个 JavaScript 文件和一些图片:
<!doctype html>
<html lang="en">
<head>
<title>Page Not Found</title>
<link rel="stylesheet" href="css/screen.css">
<script src="js/main.js"></script>
</head>
<body>
<img src="img/photo1.jpg" width="500" height="500" alt="an image" />
<img src="img/photo2.jpg" width="500" height="500" alt="an image" />
</body>
</html>
浏览器采取的第一步是下载 HTML 文件;下载完成后,浏览器将开始解析 HTML 文档。
在现代浏览器中,有两个独立的解析器用于 HTML 解析:第一个是预解析器(也称为推测解析器),第二个是主 HTML 解析器。
当预解析器通读 HTML 时,它将试图找到任何需要下载的资源来呈现页面。在给出的例子中,预解析器将找到样式表,然后是 JS 文件,接着是两个图像。在找到这些资源中的每一个时,预解析器将从网络请求它们。值得注意的是,预解析器只会解析对资源的引用,而不会构造或修改 DOM 树。
当解析器在 HTML 文档中寻找需要的资源时,浏览器的主解析器开始解析 HTML,用它来构建 DOM 树。
当主 HTML 解析器解析 HTML 时,它不会在找到样式表时停止,因为它不会被阻止继续解析 HTML 文档,因为 CSS 无法对 DOM 树进行任何更改。虽然浏览器在下载 CSS 文档时不会阻止 HTML 文档的解析,但它会阻止页面的呈现,直到 CSS 文档完全下载完毕。同样重要的是要意识到,在某些浏览器中,当浏览器等待 CSS 完全下载时,脚本可能会被阻止。
如果主 HTML 解析器遇到一个 JavaScript 文件,情况会略有不同。尽管浏览器的解析器已经向服务器请求了文件,并继续解析页面的其余部分,但是主 HTML 解析器需要等到文件完全下载完毕。这是因为 HTML 解析器必须考虑这样的情况,即当 HTML 解析器到达<script>
标记时,JavaScript 作者可能期望脚本被执行。这意味着 HTML 解析器将停止构建 DOM,直到 JavaScript 下载并执行完毕。这就是 JavaScript 阻塞这个术语的来源。
改进关键渲染路径
了解了什么是关键呈现路径之后,让我们看看如何改进网站的关键呈现路径。有几个需要关注的关键领域,这些将在接下来的章节中解释。
减少阻塞
如前所述,当主浏览器解析器发现一个脚本标记时,它将停止解析页面,以便 JavaScript 可以执行。
有两种方法可以避免这种阻塞行为:
Move all the scripts to the bottom of the HTML document. Add the defer
attribute to the script tag so the browser will execute after the document is parsed.
这在你自己的脚本中很容易实现,然而,通常你需要在网站中包含第三方脚本,它们只是提供需要嵌入的代码。可能需要包含的第三方脚本示例如下:
Social share buttons Analytics
可以添加到网站的社交按钮通常是通过在网站中您希望使用它们的部分添加小代码片段来实现的。这里的问题是,它们通常要求站点加载一些外部脚本,这可能会阻止解析或页面加载事件,直到它完成加载。对此的一个解决方案是将社交按钮的加载推迟到页面完全加载之后。这可以使用一个名为 Socialite.js ( http://socialitejs.com/
)的 JavaScript 库来实现,它可以让你灵活地选择何时加载你的分享按钮。
当谈到分析包时,他们通常会建议你将分析代码包含在你的head
元素中或者在你身体的顶部。这样做的主要原因是,他们希望确保您的用户被跟踪,即使他们在页面完全加载之前就离开了。虽然一些分析包确实提供了异步版本的代码,以防止在等待加载包时阻塞,但由于浏览器可以进行的最大数量的同时连接,它仍然可能阻塞其他更重要的文件。
移除未使用的 CSS
在你的站点上没有使用的 CSS 会以几种不同的方式影响你的站点的性能,首先是它增加了你的 CSS 文件的大小,这导致了更长的下载时间。
您需要确保删除未使用的 CSS 的第二个原因是,在解析 HTML 时,浏览器会构建 DOM 树。构建完 DOM 树后,浏览器的 CSS 引擎将遍历树中的每个元素,并将元素与指向它的任何选择器进行匹配。CSS 引擎需要评估的选择器越多,对渲染性能的影响就越大。
指定图像尺寸
当浏览器开始布局您的网页时,您包含在网页中的部分(如果不是全部)图像将不会完成下载。如果您没有指定图像的大小,浏览器将无法为您的图像布局正确的空间量,因此当您的图像下载完成时,浏览器需要执行内容的回流并重新绘制页面的该部分。
通过指定图片的大小,无论是通过 CSS 还是通过在你的img
元素上定义它,浏览器都知道图片的大小,这将避免图片下载完成后不得不重排内容的问题。
延迟加载图像
我已经提到过在页面完全加载后再加载社交按钮,然而,一个网站也可以从在页面加载后再加载图片中获益。
选择延迟您的图像稍微复杂一些,因为您需要做出几个选择。您将需要确定哪些图像应该被延迟,哪些图像需要一直在页面中,并且您还需要决定延迟的图像将在何时被加载。
一种流行的延迟图片的方法是当用户可以看到图片时才加载它们,这里的想法是如果用户从不向下滚动页面,他们的浏览器就不需要下载图片。要以这种方式实现延迟图像加载,您可以使用一个名为 Echo.js ( https://github.com/toddmotto/echo
)的库,它将允许您使用 loader GIF 作为实际图像的src
和数据属性来定义图像。当图像进入视图时,它将加载图像并用实际图像替换加载的图像。
服务器端优化
针对站点支持的不同设备优化响应站点的方法之一是使用服务器端设备检测。这意味着在您的服务器上,您可以检测您的用户正在使用哪个设备,并基于此执行操作。
服务器可以优化什么?
您选择使用的任何服务器端优化都应该侧重于优化您交付给用户设备的有效负载。考虑到这一点,让我们把重点放在服务器端优化上,看看图像和内容块。
形象
您可以检测设备服务器端,然后向设备提供优化的图像,而不是交付旨在跨多种设备类型查看的通用图像。
内容块
即使在一个响应式站点上,也有可能需要为不同的设备提供不同的内容块,这有几种方法可以实现。第一种方法是使用媒体查询简单地显示和隐藏它,缺点是您向用户提供了两组内容。
另一种选择是使用服务器端代码来确定应该在特定设备上显示哪个内容块,并且只包括页面的 HTML 中的内容。这样做的好处是,您只需提供用户设备所需的内容。
实现服务器端优化
要实现任何类型的服务器端优化,您需要能够确定用户正在使用哪种设备。您受到浏览器提供的信息的限制,它们提供的主要信息来源是用户代理字符串。
多年来,作为开发人员,我们被告知“用户代理嗅探是不好的”,这是有充分理由的;用户代理字符串很容易被欺骗,并且具有浏览器伪装成其他浏览器的历史,因为他们希望他们的用户获得与这些其他浏览器中的用户相同的体验。有些边缘情况下,我们不得不使用用户代理嗅探,因为没有其他可用的东西,服务器端优化就是其中之一。
虽然通过基本的用户代理嗅探来进行设备检测非常容易,但是有大量的设备可用,并且每个设备都有自己唯一的用户代理字符串(有几个例外)。因此,跟踪自己要嗅探的不同用户代理可能很困难。这是您可以使用设备描述库(DDR)的地方。DDR 本质上是关于不同设备的信息目录,其中包括各种各样的信息,包括它运行的操作系统、设备的视窗大小以及它支持的功能。
有三种著名的 DDR:WURFL、OpenDDR 和 Apache DeviceMap。让我们更详细地看看每一个选项,以便您可以选择哪个选项适合您的项目。
伍飞
WURFL(Wireless Universal Resource FiLe)(http://wurfl.sourceforge.net/
)是一个 DDR,由一家名为 ScientaMobile 的公司开发,该公司开发并维护 DDR 和用于与之接口的 php 库。
WURFL 检测到的每个设备都有一个配置文件来指示设备的功能。这使您能够检测设备,并在成功确定设备后,选择应该为设备提供哪些资产。
WURFL 存储库由 ScientaMobile 维护,它还提供一个付费的托管解决方案,名为 WURFL Cloud。使用 WURFL 托管版本的好处是,您不需要负责确保设备列表保持最新。
打开目录
创建 OpenDDR ( http://www.openddr.org/
)的目的是构建一个总是最新的设备描述。它是 WURFL 的替代方案,因为它不是由一个单独的实体来维护,而是由整个社区来开发和维护。OpenDDR 的数据库最初是基于一个旧的 WURFL 数据库,自从最初的 WURFL 项目上的许可被改变以来,这个数据库一直由 OpenDDR 团队独立维护。
Apache 设备地图
Apache DeviceMap ( http://incubator.apache.org/devicemap/
)是一个孵化项目,旨在创建一个存储设备信息的开放资源库。它与 OpenDDR 团队的目标相似,这导致 OpenDDR 团队同意将 OpenDDR 代码库捐赠给 DeviceMap。这意味着,尽管此时 DeviceMap 项目还处于早期阶段,但在未来,如果您希望在服务器上进行设备检测,我们应该可以期待它会成为一个流行的选项。
服务器端优化的优势
使用服务器端优化技术有很多好处,首先是您可以根据用于查看图像的设备的大小来定位图像资产。这意味着,您可以选择首先将较小的图像发送到设备,使您的网站在较小的设备上加载更快,而不是简单地为较小的设备缩放图像。
除了简单地针对不同设备的图像,您还可以针对整个代码块,这意味着您可以轻松地交换出依赖于设备的模块。这种方法允许您在最合适的地方混合响应性设计方法和适应性设计方法。
服务器端优化的缺点
使用服务器端优化的最大缺点是,它通常依赖于用户代理嗅探,这意味着当新设备或新浏览器发布时,您需要确保定期更新您选择的库。虽然这里讨论的库通过定期更新以支持新设备来帮助缓解这个问题,但是您将依赖对这些库的持续维护来允许您继续支持新设备。
类似地,我们可能会发现将来发布的设备与另一个设备共享同一个用户代理。这可能会导致我们如何使用服务器端优化来定位代码的问题,因为它可能会错误地识别设备并发送不正确的内容。这是有先例的,不仅在移动设备上,在 web 浏览器的早期也是如此,在 web 浏览器中,浏览器相互欺骗用户代理字符串,以克服用户代理嗅探的情况,这在其网站上反映很差。
第三方服务器端解决方案
除了编写自己的服务器端解决方案,还可以选择使用第三方服务来为您处理这些服务器端优化。
ReSRC.it 就是这样一个服务,它允许您通过 JavaScript 和服务器端代码的组合向站点添加响应图像。首先在图像的路径前面加上对 ReSRC.it 服务器的调用,然后简单地包含 ReSRC.it 脚本,该脚本将处理向用户显示正确的图像:
<img src="//app.resrc.it/s=w280/o=60(80)/
http://www.your-domain.com/image.jpg
<script src="//use.resrc.it"></script>
通过使用这种服务,您可以避免构建自己的后端解决方案,但是,这种服务的局限性在于,它需要 JavaScript 来加载图像的正确版本。如果 JavaScript 加载失败,用户将只能看到质量较低的图像。
衡量你的网站的表现
为了能够成功地优化您的站点,您需要能够衡量您的站点的性能以及您所做的任何更改的性能增益。
有各种各样的工具可以让你测试你的站点的性能,所以让我们来看看这些工具中的一些,看看当你试图优化你的站点的性能时,它们是如何给你带来好处的。
Pingdom 网站速度测试
可以用来测试网站性能的工具之一是 Pingdom 网站速度测试( http://tools.pingdom.com/fpt/
,它允许你输入你的网站的 URL 来测试性能,如图 10-11 所示。
图 10-11。
The Pingdom Website Speed Test, just enter your URL and click Test Now
单击“立即测试”按钮后,该工具将分析站点并发现任何潜在的性能问题。开始运行测试后,你会看到一些关于你的站点的基本性能信息(如图 10-12 所示),包括你的站点发出的 HTTP 请求的数量、加载时间和你的页面大小。
图 10-12。
The basic performance grade for the site
瀑布
在摘要下面,瀑布图显示了网页向服务器发出的 HTTP 请求。瀑布视图的示例如图 10-13 所示。每个请求都有颜色编码:
Pink: DNS Blue: Connection to server Orange: Browser sends request to the server Yellow: Browser waits for response from server Green: The browser is receiving data from the server
图 10-13。
The Waterfall of the performance of the site shown in Pingdom
绩效评分
Pingdom 还提供了查看不同性能领域的个人性能分数的能力。作为开发者,这可以让你很快看到你的网站做得很差的地方,这样你就可以纠正它们,因为如果你修复它们,这些地方可能会给你带来最高的性能收益。我的博客( www.jonathanfielding.com
)的表现评分如图 10-14 所示。
图 10-14。
Pingdom performance scoring for my blog
页面分析
Pingdom 还提供了一些进一步的页面分析数据,这些数据在查看站点性能问题时非常有用。它提供的数据可以分为三类:加载时间分析、大小分析和请求分析。我的博客的完整页面分析如图 10-15 所示。
加载时间分析是与页面加载时间相关的数据;它包括站点在瀑布图中显示的每个状态下花费的时间,加载每个内容类型花费的时间,以及从每个域加载花费的时间。后者可能特别有用,因为它允许您轻松地看到由第三方服务(如您网站上包含的广告)导致的页面加载瓶颈。
大小分析数据提供了页面权重来源的详细分类。特别是,它显示了每种内容类型的大小,这表明哪种类型的内容最大。它还显示了从其他域提供的内容的大小,当您试图确定为什么您的站点看起来很大,而您觉得自己的文件大小合适时,这很有用。
请求分析信息与您的站点必须发出的请求数量有关。这分为两种类型的数据:每个内容类型的请求数和每个域的请求数。同样,这允许您查看第三方插件是否正在创建您可能没有意识到的过多的 HTTP 请求。
图 10-15。
Pingdom full page analysis
进行优化
YSlow ( https://developer.yahoo.com/yslow/
)是雅虎开发的工具这允许你测试你的网页的性能。它可以作为浏览器插件安装,这意味着您可以在自己的浏览器中直接测量网页的性能。
要开始使用 YSlow,请访问 http://yslow.org/
,在那里您可以选择您的浏览器并安装 YSlow 插件的相关版本。安装完成后,访问您的网站并单击浏览器中添加的 YSlow 图标。图标如图 10-16 所示。
图 10-16。
Chrome toolbar, the YSlow icon is highlighted
单击 YSlow 图标后,会出现一个弹出窗口,向您提供 YSlow 的一些信息。如果您的站点阻止自己嵌入 iframe,那么您需要确保选中了此页面上的复选框。要运行 YSlow 测试,只需点击运行测试按钮,如图 10-17 所示。
图 10-17。
The YSlow panel
一旦 YSlow 运行了它的测试,你就可以浏览每一个标签来决定你的站点运行的如何。
级别
一旦 YSlow 运行了它的测试,你将被带到 Grade 选项卡,在这里 YSlow 将根据你的站点的性能给它打分。它还给出了你在 23 个不同领域的分数,你可以点击这些分数来获得关于你为什么得到那个分数以及你可以做什么来提高它的更多信息。在我的博客上运行测试后的成绩视图如图 10-18 所示。
图 10-18。
The YSlow Grade panel for my blog
成分
YSlow 的 Components 选项卡提供了关于组成站点的各个资源的信息。它们按类型排序,您可以通过扩展类型来查看各个文件,从而找到关于每个资源的更多信息。
查看单个文件时,您可以看到有关文件大小、文件头以及随文件发送和接收的 cookies 的信息。这可能是有用的,因为这意味着你可以很容易地看到,如果你发送大量的 cookie 数据与您的文件,所以你可以解决这个问题,同时优化您的网站。我的博客的组件信息如图 10-19 所示。
图 10-19。
The YSlow Components panel for my blog
统计数字
YSlow 的 Statistics 选项卡提供了一些图表,显示了每种内容类型的请求所占的百分比,以及每种内容类型的 HTTP 请求数量。我的博客的统计面板如图 10-20 所示。
图 10-20。
The YSlow Statistics panel for my blog
网页测试
WebPageTest ( http://www.webpagetest.org
)是一个测量页面性能的免费工具,最初由 AOL 开发供内部使用,但在 2008 年在 BSD 许可下开源。该工具的托管版本由 WPO 基金会运营,该基金会是一家非营利性企业,旨在提高网站性能。
WebPageTest 使您能够在世界各地的许多不同位置,在许多不同的浏览器中,在您的站点上运行 web 性能测试。它还允许您通过自定义高级设置来运行一些高级测试。
要开始使用 WebPageTest 测试您的站点,您需要访问他们的网站并输入您站点的 URL。你可以选择一个测试地点,这意味着你可以选择你的目标市场的位置,让你看到你的真实用户访问你的网站时的表现。此外,您可以选择用于运行性能测试的浏览器。WebPageTest 的一些特性并不是在所有的浏览器上都可用,所以您可能需要在多个浏览器中运行测试来利用所有的特性。使用 WebPageTest 运行测试的起始页如图 10-21 所示。
图 10-21。
Initial start page of WebPageTest, with the option to select browser and advanced options
因为 WebPageTest 站点是一个受欢迎的服务,所以您可能需要排队等候才能使用它。然而,通常你不用等超过几分钟。
一旦加载了结果,与其他测试类似,WebPageTest 会给你的站点一个性能等级。此外,该页面还显示了关于在第一次查看和重复查看时加载网站所用时间的信息。我的博客的绩效等级如图 10-22 所示。
图 10-22。
The WebPageTest performance grade for my blog
瀑布景观
运行完这个测试后,您可以单击显示的瀑布图像,进入瀑布视图。该视图显示了加载每个文件所用时间的详细信息,包括 DNS 查找、初始连接、SSL 协商、首字节时间和内容下载。
这个视图非常有用,可以让您看到资产加载受阻的任何地方,或者资产转移的任何问题。我的博客的瀑布视图结果如图 10-23 所示。
图 10-23。
The WebPageTest Waterfall View after testing my blog
高级测试
WebPageTest 在衡量网站性能的工具中真正脱颖而出的地方在于它的可配置性。我的意思是,在设置您的性能测试时,您可以从几个高级选项中选择各种选项。
要选择这些高级选项,您只需单击“高级设置”链接,这将展开站点的这一部分,以便您可以选择所需的高级选项。高级选项分为以下选项卡:
Test Settings Advanced Chrome Auth Script Block SPOF
虽然我不会详细解释所有这些选项,但最重要的设置之一是第一个(测试设置)选项卡上显示的连接设置。通过允许您选择不同的连接速度,此设置使您作为开发人员能够在用户可能面临的条件下测试网站。可供选择的范围从快速电缆连接到 56K 调制解调器。当决定测试哪些速度时,你需要对你的目标用户可能使用的速度持现实态度。用于选择这些高级选项的菜单如图 10-24 所示。
图 10-24。
The WebPageTest advanced testing menu
谷歌分析网站速度
尽管确保在测试环境中一切正常对于确保一个站点对最终用户表现良好大有帮助,但我们无法测试每一个用例,因为有各种设备连接的组合。
在第九章中,你学习了如何使用 Google Analytics 来衡量用户的旅程,Google Analytics 可以帮助你的另一种方式是查看你的网站对最终用户的实际表现。
当使用 Google Analytics 查看一个站点对最终用户的表现时,你可以从查看站点速度概览页面开始。默认情况下,该页面显示以下信息:
Average page load time Average redirection time Average domain lookup time Average server connection time Average server response time Average page download time
此信息与上个月的平均值一起显示,并带有一个图表,显示该值在所选时间段(默认为月)内的波动情况。现场速度概览页面如图 10-25 所示。
图 10-25。
Google Analytics Site Speed Overview
除了总体概述,Google Analytics 还能够就如何提高页面性能给出一些建议。这可以在网站速度建议页面上找到。初始视图包括一个页面列表,其中有一个标题为“速度建议”的列,每行都有一个链接,您可以单击该链接查看该页面的建议(如图 10-26 所示)。
图 10-26。
Google Analytics site Speed Suggestions
摘要
本章讨论了优化网站性能的方法。因为性能是一个很大的主题,所以我把重点放在了对一个响应性网站有最大影响的方面。
首先讨论的领域之一是网络性能的重要性,现在您应该对延迟和带宽以及它们如何影响您站点的网络性能有了很好的理解。然后,我讨论了如何通过使用允许您减少 HTTP 请求和优化服务器设置的技术来优化您的站点,以最好地应对这些限制。
然后,我解释了如何通过使用服务器端技术来确定向站点用户提供什么服务,从而优化站点性能。这包括查看如何使用设备描述库来确定用户正在使用的设备类型,以便为他们提供优化的内容。
虽然了解优化性能的方法使您能够使用性能响应技术来构建您的站点,但是能够度量性能也很重要。为了让您能够做到这一点,我介绍了两种您可以在站点开发过程中使用的工具:测量工具 WebPageTest、Pingdom 和 YSlow,以及生活工具,即在用户设备上测量站点性能的工具,特别是 Google Analytics 站点速度工具。
希望这本书能帮助你努力构建响应性网页。通过这本书,你将了解到从头开始创建一个网站或者改造一个现有网站的步骤。通过将这些技能应用到你的工作中,你将有助于改善用户在使用我们网站时的体验。
我真的希望你喜欢读这本书,就像我喜欢写这本书一样,并且当你想尝试不同的技术或改进你的响应网站时,你可以回头看看这本书作为参考。
Footnotes 1
2
3
http://www.webperformancetoday.com/2013/12/11/slower-web-pages-user-frustration/
。
4
5
http://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/
。
6
http://chimera.labs.oreilly.com/books/1230000000545
。
7
http://www.chromium.org/spdy/spdy-whitepaper
。
8
https://developers.google.com/speed/docs/best-practices/rtt
。
9
如果你有兴趣阅读浏览器用户代理字符串的历史,你可以在 http://webaim.org/blog/user-agent-string-history/
找到它。