HTML5-和-CSS3-响应式-Web-设计入门指南-全-

HTML5 和 CSS3 响应式 Web 设计入门指南(全)

原文:Beginning Responsive Web Design with HTML5 and CSS3

协议:CC BY-NC-SA 4.0

一、响应式设计导论

在过去的 20 年里,网站的构建方式已经发生了变化。20 世纪 90 年代,以表格形式构建的网站占主导地位,尽管级联样式表(CSS)规范于 1996 年发布,但 CSS 直到 2003 年中期才真正占据中心舞台。这带来了 CSS Zen Garden 的推出,它展示了 CSS 的力量,以及如何用它来完全重新设计一个站点,甚至不需要接触 HTML。随着后来 CSS 的流行,开发社区标准化了分辨率为 1024×800 的目标屏幕,较大的屏幕边缘留有空白,较小的屏幕需要滚动。这是为了使网站的设计和建设能够为尽可能多的受众服务,因为大多数用户都是通过 1024×800 的分辨率访问网站的。

随着 2007 年 iPhone 的推出,互联网的完整体验随处可见。过去简单、难用的移动浏览器一去不复返了,突然间我们口袋里有了一个完整的桌面级浏览器。各公司的第一反应是创建一个独立的、针对移动设备进行优化的网站,他们认为提供有针对性的用户体验会增加销售额。通常情况下,这些网站是完整网站的缩小版,往往无法向访问者提供他们想要的内容,这意味着最终他们要么离开网站,要么转向完整的网站。

2010 年,手机浏览器实现了 CSS3(层叠样式表 3)媒体查询,从 Android 2.1 开始,到 iOS 3.2。媒体查询的到来带来了针对不同屏幕分辨率的特定风格的能力。

除了屏幕尺寸的变化,随着高像素密度屏幕变得越来越普遍,也有一个巨大的驱动力来提高正在使用的屏幕的质量。retina display 一词是苹果公司在 2010 年 6 月构想出来的,用来描述他们自己手机上的高 dpi(每 CSS 英寸点数)屏幕,他们被认为是将高 dpi 屏幕带入主流的人。因为他们不是唯一部署该技术的公司,所以在构建过程中考虑高 dpi 显示变得越来越重要,以确保您的网站在这些设备上看起来非常棒。确保和实现这一点的最佳方式是通过使用响应式设计方法。

响应式设计已经迅速成为当前 web 开发的趋势,本书旨在带您了解实现响应式设计的不同方法。本章将向你介绍响应式设计。本章包含的部分有:

What is responsive design?   Why is mobile so important?   Responsive design vs. device-specific experiences   Responsive web design is not limited just to mobile   When would you not use responsive web design?   Examples of responsive web design   Looking at HTML5 technologies   What’s new in CSS3

什么是响应式设计?

响应式设计这个术语来源于浏览器对其环境做出反应的方式。响应式设计是一种开发网站的方法,旨在为网站用户提供良好的体验,而不管使用什么浏览器、设备或屏幕大小。使用响应式设计方法设计的网站通过使用流动网格、流动内容(例如,图像、视频和文本)和 CSS3 媒体查询来调整其布局。

响应式设计不再使用像素等固定单位,而是更倾向于百分比等相对单位。这意味着站点不同部分的宽度被设计为视口的百分比。

伊桑·马科特在他的文章《一份清单之外》中首次创造了响应式网页设计这个术语,他将网页比作建筑。他提出了一个关键观点,即我们应该如何将日益增多的网络设备视为同一体验的不同方面。

我们可以把越来越多的网络设备视为同一体验的不同方面,而不是为每一个设备量身定制不相关的设计。我们可以为最佳的视觉体验而设计,但是将基于标准的技术嵌入到我们的设计中,使它们不仅更加灵活,而且更加适应呈现它们的媒体。简而言之,我们需要练习响应式网页设计。1

Ethan Marcotte, off the list.

Ethan 在这里建议的是所有的显示器应该接收相同的内容。然而,它需要被构建为灵活的,以便正确地适合显示器。网站应该以优化设备体验的方式进行调整。

为什么移动如此重要?

随着智能手机的进步,人们可以在任何时候从口袋或包里简单地取出东西来访问互联网。从在当地电器商店查看您感兴趣的电视机的评论,到找到最近的比萨饼店,互联网不再要求您被束缚在一台有过多电缆的计算机上,但它可以与您形影不离。考虑到这一点,开发一个不适合在移动设备上运行的网站的想法是荒谬的。

智能手机市场不再是手机行业的利基部分,而是随着普及而蓬勃发展,智能手机占 2013 年全球手机销量的 57.6%。虽然你可能会认为这些销售中的一部分只是智能手机作为手机计划的一部分的结果,但这仍然是一个惊人的数字。

关于智能手机市场的增长,最有趣的事情之一是来自移动设备的网络流量的百分比正在迅速增加,WalkerSands Digital 估计仅在 2013 年移动流量就增加了 67%(见图 1-1 )。当我们更详细地查看这些统计数据时,我们可以清楚地看到,移动的增长不容忽视。

A978-1-4302-6695-2_1_Fig1_HTML.jpg

图 1-1。

Percentage of web traffic coming from mobile devices in 2012-2013

需要记住的一点是,这些数据并没有将商业和消费者流量分开。如果数据是专门针对消费者流量的,我们可能会发现来自移动端的流量比例要高得多。我们之所以会有这样的预期,是因为在工作时间,大多数用户会使用台式电脑来访问互联网。

2013 年 11 月,乔·麦肯在布莱顿正面全裸的演讲中讨论了移动对大型零售商的预期在线影响。在谈到移动的重要性时,他提到了他与一位来自 Target.com 的人就移动对其业务的影响进行的讨论,他的说法支持了我们的预期,即移动的消费者流量将高于商业和消费者流量的总和。他说:

“今年,预计有史以来第一次有超过一半的 Target.com 流量来自移动设备。”3

—— Joe McCann, creative and technical director of new york's mother, August 11th, 2013

这将是一个令人难以置信的数字,随着移动设备的使用越来越普遍,预计这一数字还会继续上升。移动设备有潜力实现真正的无处不在,这可能是台式电脑和笔记本电脑永远不可能实现的。原因有两个:首先,入门成本低得多,现在购买平板电脑不到 50 美元,购买智能手机不到 30 美元。第二,触摸界面比桌面界面更直观,这意味着以前在使用电脑时可能有问题的人更有可能使用移动平台。虽然现在有触摸界面的台式机和笔记本电脑,但这些都被认为会导致手臂劳损,特别是在肩部,使移动平台更具吸引力。 4

移动市场好转的另一个指标是移动电话的销售数据。它们揭示了智能手机市场尚未饱和,50 亿手机用户群中只有 15 亿是智能手机。此外,2012 年第四季度,平板电脑的销量超过了台式电脑和笔记本电脑的总销量。 5

响应式设计与特定设备体验

正如刚才所讨论的,移动是一个巨大的增长领域,您可能会问,为什么我们不针对我们的目标平台量身打造特定于设备的体验。

当比较响应式网站和独立网站时,很容易断言一个独立的网站会让你提供更好的体验。这是雅各布·尼尔森(Jakob Nielsen)的观点,他写的一篇文章的摘要如下:

“在不同的媒体形式之间重复使用内容和设计,比如平面媒体与网络媒体,或者桌面媒体与移动媒体,虽然成本低廉,但却有失身份。卓越的 UX 需要紧密的平台集成。”6

—Jacob Nelson

事实上,我认为这是一个被误导的观点。响应式开发允许您定义移动设备接收的用户体验,因此,可以通过隐藏和显示不特定于平台的内容来调整内容。

响应式网站设计的主要好处之一就是简单。不需要单独的移动存在,因为 responsive 允许使用相同的 URL 和相同的代码库。有了一个代码库,测试变得更简单,如果您在使用测试驱动开发的工作场所工作,这尤其有用,因为两个代码库可能会导致需要更多的单元测试。

这种简单性的一部分是,使用响应式设计,您只需管理一批内容,而不是本质上管理多个站点上的相同内容。这在网站内容需要由几个人或法律团队批准的组织中尤其重要。这当然会加快速度,从而节省时间和金钱。

对于大多数网站来说,在搜索引擎上获得好的排名是很重要的,谷歌提供了他们希望你如何建立网站的指导。作为他们指南的一部分,Google 建议使用以下注释进行响应式开发:

A single URL for content makes it easier for your users to interact with and share the content.   A single URL for content helps Google’s algorithms index your site.   No redirection or server side device detection is needed for users to get to the device-optimized view, which reduces loading time.   Googlebot user agents have to crawl your pages once, as opposed to crawling multiple times with different user agents, to retrieve your content.

有了这些建议,走响应路线就更有意义了,尤其是如果你的业务依赖于通过谷歌找到。

在权衡使用响应式网站设计或独立网站的利弊时,考虑如何通过更新、修改和添加新功能来继续支持网站也很重要。两个代码库需要两倍的工作、时间和精力来更新和支持。

如果你已经有了一个满意的网站,你可以考虑转换你当前的网站,而不是完全重建。虽然这种方法首先不是移动的(所以根据定义,你将采取一种优雅的降级方法,而不是渐进的增强方法),但它可能允许你使你的站点响应更快。转换现有的站点部分包括重构现有的代码和向 CSS 添加媒体查询。

这种比较似乎严重倾向于响应性开发;但是,单独的站点构建也有一些好处。首先,优化移动站点的性能要容易得多,因为您不需要担心桌面站点所需的媒体查询、JavaScript 和 JavaScript 库的开销。此外,拥有单独的站点构建意味着您不需要接触现有的桌面站点,这反过来意味着不需要重新构建和重新测试。

响应式网页设计不仅限于手机

到目前为止,很多关于响应式 web 设计的讨论都集中在响应式开发如何让你构建在移动设备上运行良好的网站。然而,不仅仅是移动设备可以从响应式网页设计技术中受益。

尽管基于网络的电视服务如 BBC 的 iPlayer、网飞和亚马逊的 Lovefilm 都可以在移动设备上使用,但电视仍然是家庭娱乐的中心。电视最常见的用途是消费媒体:观看电视节目、在视频游戏控制台上玩游戏,或者只是用来播放背景音乐或听广播。

2013 年 4 月,德勤的媒体和娱乐业务进行了一项调查,发现现在 50%的家庭都可以找到视频游戏机;他们还发现,26%的电视直接或通过机顶盒(机顶盒的例子包括游戏控制台、媒体电脑)连接到互联网。在展望这一领域的未来增长时,我们还需要记住,自 2012 年 10 月以来,所有主要的游戏主机都包含网络浏览器,这意味着随着更多用户将这些设备连接到互联网,还有进一步增长的潜力。

除了电视,分辨率更高、显示器更大的台式机或笔记本电脑正变得越来越普遍。如前所述,历史上的网站宽度是以 1024×800 分辨率的屏幕为目标的,然而,截至 2012 年 3 月,1366×768 屏幕已经成为最常见的分辨率。使用响应式技术,你可以利用这些额外的空间,而不仅仅是在网站的两边留有很大的空白。图像可以更大,内容可以间隔更大,用户甚至可以在开始滚动之前看到更多的内容。

如果我们只看一小部分设备,很容易发现屏幕分辨率各不相同。图 1-2 显示了来自同一个制造商(本例中为苹果公司)的移动设备的不同屏幕分辨率,以及最常见的屏幕分辨率和常见的电视分辨率。

A978-1-4302-6695-2_1_Fig2_HTML.jpg

图 1-2。

Screen resolutions of Apple devices

正如这个简单的例子所展示的那样,仅一家设备制造商就需要支持多种分辨率,当您考虑到其他制造商的大量设备时,现在常见的不同分辨率的绝对水平是惊人的。我们还必须记住,具有新屏幕分辨率的新设备会定期被开发和发布,所以你需要确保你的网站足够灵活,能够与这些新设备一起工作,不管它们是什么。

响应式设计不仅仅是简单的移动与桌面的对比;因此,当您考虑响应式设计时,重要的是不要简单地从这些方面考虑,而是要考虑如何让您的设计在尽可能多的设备上工作,而不管屏幕大小和它们的功能如何。一个很好的例子是,不要假设所有移动浏览器都支持地理位置 API(应用程序编程接口),您可以使用功能检测来识别用户浏览器支持的功能,并逐步增强站点。

什么时候你不会使用响应式网页设计?

有时,使用响应式设计技术并不总是合适的,但相反,对于用户体验来说,构建特定于设备的体验会更好。

响应式设计不适合的一个主要例子是在浏览器中提供类似桌面体验的 web 应用程序。谷歌文档就是这样一个网络应用,在桌面浏览器上你可以得到一个全功能的文字处理器,但由于这种体验在移动浏览器上无法实现,你只能得到一个大大简化的移动版本。原因是对于像 Google Docs 这样功能丰富的网络应用程序来说,小屏幕尺寸是一个很大的挑战。在一个更大的视窗中,很容易将所有的功能放到一个工具栏中,但是,在移动设备上,这是不可能的。因此,为了提供更好的用户体验,将界面剥离回典型用户实际使用的界面是有意义的。这种条带化的后界面将与桌面界面非常不同,以允许代码库精简,这样移动和桌面体验分开构建就有意义了。

除了大型 web 应用程序之外,如果您希望转换现有的站点而不是重建它,查看现有的代码库以确保它不会臃肿是很重要的。只有在现有的代码库相当精简的情况下,才应该将现有的代码库转换为响应式构建。如果你发现你现有的网站过于臃肿,你可以选择在转换网站之前花时间精简它;但是,如果这是不可能的或者预算不允许重建,您可以选择单独构建移动站点。

了解视口

响应设计中的一个重要概念是视口。顾名思义,视口是用来查看网站的视图。

在 HTML5 和 CSS3 之前,我们通常认为网站与浏览器窗口的大小有关。通常情况下,我们的用户会使用 1024×800 的最小显示尺寸,窗口全屏显示,因此,我们会将我们的网站构建为固定的宽度,通常在 960px 到 980px 之间。然而,在开发早期的小型设备时,制造商面临一个问题。当时大多数网站都是这样固定宽度的,比他们新设备的屏幕宽度要宽得多。如果他们以设备的原生分辨率加载站点,那么用户将需要水平和垂直滚动来查看站点。

解决这个问题的方法是将视窗宽度设置为大于设备宽度,这意味着站点将被缩放以适合屏幕。例如,iOS 默认将视窗宽度设置为 980 像素,这样,典型站点的整个宽度将适合屏幕,而无需水平滚动。因此,网站将被缩小,因此,为了阅读网站的内容,用户将放大他们感兴趣的内容。这为旧网站提供了最佳的折衷方案,以确保它们可以在较小的设备上使用。

为了让开发人员控制视口宽度,引入了一个 meta 标签,它允许设置视口宽度和初始比例;我将在本章后面介绍如何使用这个 meta 标签。这意味着您可以告诉移动浏览器以不同的视口宽度呈现站点。在使用响应式设计技术的情况下,您可以选择告诉浏览器将视窗宽度设置为等于浏览器窗口的宽度(或者在单个窗口设备的情况下,等于该设备的宽度)。

图 1-3 显示了视窗宽度和视窗高度相对于浏览器窗口的测量位置。

A978-1-4302-6695-2_1_Fig3_HTML.jpg

图 1-3。

A diagram illustrating the viewport width and viewport height

多种多样的设备意味着您需要确保在多种不同的视窗尺寸下进行测试。要轻松查找各种流行设备的视口大小,您可以查看 http://viewportsizes.com ,它允许您搜索设备列表,以及设备视口大小的信息。

了解断点

除了理解视口,您还需要很好地理解什么是断点。响应式设计中的断点是网站根据媒体查询声明更改布局的宽度。典型地,响应站点将被构建成与针对特定类型的设备的至少两个但通常是三个不同的断点一起工作。最常用的断点有:

Extra small devices, for example, Phones (<768px)   Small devices, for example, tablets (≥768px and <992px)   Medium devices, for example, desktop computers (≥992px and <1200px)   Large devices, for example, desktops computers (≥1200px)

除了断点,您需要理解的另一个重要术语是状态,即每个断点之间的站点版本。因此,移动、平板和桌面是您的状态,在它们之间有两个断点。

请务必记住,媒体查询响应视窗的宽度,而不是屏幕的宽度。这就是为什么你可以简单地调整你的浏览器来测试你的断点。

响应式网页设计的例子

在编写响应式网页之前,让我们看看一些响应式网站的最佳例子。最好在网上访问这些网站,看看它们所描述的功能。截至本文撰写之时,在此对它们进行了描述。

八月

我们的第一个例子是八月( http://www.agst.co/ ),这是一个发现世界上最有才华的艺术家的地方。该网页是一个单页网站,在页面的末尾有一个表格来记录您的兴趣。

当你调整网站大小时,你会注意到这些变化看起来非常微妙。当您查看每个断点之间发生的变化时,您会注意到下面几节中讨论的差异。

大中型设备

对于大型设备和中型设备状态,August 使用 HTML5 视频循环播放。背景被拉伸到全宽,而内容在容器中居中。当你向下滚动页面时,你会发现这个网站的图片非常多,图片小心翼翼地包裹在文本周围(见图 1-4 和 1-5 )。

A978-1-4302-6695-2_1_Fig5_HTML.jpg

图 1-5。

The imagery on the “august” site wraps around the copy on large and medium devices

A978-1-4302-6695-2_1_Fig4_HTML.jpg

图 1-4。

The initial view of the “august” site, with the video playing in the background

小型设备

对于较小的设备(如平板电脑),August 禁止在后台播放视频,而是选择用图像来代替(见图 1-6 )。

A978-1-4302-6695-2_1_Fig6_HTML.jpg

图 1-6。

On small devices, the “august” site replaces the background video with a static image

调整文字环绕图像的方式,使文字位于图像的上方,以免覆盖主要图像(见图 1-7 )。

A978-1-4302-6695-2_1_Fig7_HTML.jpg

图 1-7。

The wrapping of the copy is adapted to better fit the screen of smaller devices and not cover the image

超小型设备

在针对移动设备的最小视图上,网站通过替换不适合移动设备的图像来适应更小的设备。以视窗高度为目标的媒体查询用于进一步调整字体大小,以确保文本正确地位于页面上(见图 1-8 )。

A978-1-4302-6695-2_1_Fig8_HTML.jpg

图 1-8。

On our extra small devices, the text is resized and the imagery is sized to be fit the smaller display better

当您查看图像的变化时,您可以看到它现在被裁剪了视窗的宽度,并且文本再次被移动到图像的上方,以防止它与图像重叠(参见图 1-9 )。

A978-1-4302-6695-2_1_Fig9_HTML.jpg

图 1-9。

In the content sections, the copy is moved up above the imagery to prevent it overlapping

楠木

Nyetimber ( http://nyetimber.com/our-story/ )网站与 August 网站有很大不同,它是一个多页面响应网站。该网站有一个题为“我们的故事”的部分,它使用视差滚动效果告诉你关于业务的故事;这一页将是下面例子的焦点。

大中型设备

该公司的故事是用桌面上的视差效果讲述的,当你滚动网站时,不同的元素在不同的时间间隔出现在视图中(见图 1-10 )。

A978-1-4302-6695-2_1_Fig10_HTML.jpg

图 1-10。

The Nyetimber site starts wth an introduction to the story

如果你点击任何一个观看电影按钮,你将被带到一个充满视窗的视频(见图 1-11 )。

A978-1-4302-6695-2_1_Fig11_HTML.jpg

图 1-11。

When opened, the videos fill the viewport

当你滚动页面时,你会看到一个区域,在那里你可以将鼠标悬停在面板上,以找到关于该公司的更多信息(见图 1-12 )。

A978-1-4302-6695-2_1_Fig12_HTML.jpg

图 1-12。

Tiled panels allow the user to hover over them

小型设备

在平板电脑上,导航已降至徽标下方,视差功能已被移除(参见图 1-13 )。

A978-1-4302-6695-2_1_Fig13_HTML.jpg

图 1-13。

Instead of

视频现在是内嵌的,而不是全屏显示(见图 1-14 )。

A978-1-4302-6695-2_1_Fig14_HTML.jpg

图 1-14。

The videos are shown inline on small devices, uses the Vimeo HTML5 player for playing the videos

由于用户不能悬停在触摸设备上,面板不再具有悬停动作,而是显示为关于 Nyetimber 的信息列表(见图 1-15 )。

A978-1-4302-6695-2_1_Fig15_HTML.jpg

图 1-15。

The information panels are visible by default on small devices

超小型设备

在手机上,页眉被进一步缩小,以移除导航,并在顶部的一条带上显示徽标(参见图 1-16 )。

A978-1-4302-6695-2_1_Fig16_HTML.jpg

图 1-16。

The menu be default is collapsed on extra small devices, with a menu icon now available to toggle it

点击标题上的导航按钮,导航现在打开(参见图 1-17 )。

A978-1-4302-6695-2_1_Fig17_HTML.jpg

图 1-17。

Taping the menu icon expands the menu

其余内容的大小被调整得更窄,字体大小也更小(参见图 1-18 )

A978-1-4302-6695-2_1_Fig18_HTML.jpg

图 1-18。

The content is narrower, blocks are all stacked and the text size optimized for the device

其他示例

在 AWWWards 网站上有更多的例子,那里有一整节是关于响应式设计的。 7

HTML5 入门

响应式设计建立在 HTML5 和 CSS3 带来的新技术之上。让我们看看 HTML5 带来的变化,这样我们可以更好地理解我们正在编写的代码。

HTML5 是最新版本的 HTML 语言规范的规范草案,由万维网联盟(W3C)达成一致。HTML5 规范是 W3C 称之为“开放 web 平台”的更大技术集的一部分,简单地说,这意味着它允许我们构建在任何地方都可以工作的网站和 web 应用程序。当人们提到术语 HTML5 时,他们通常谈论的是“开放网络平台”

在您的项目中使用 HTML5 有很多好处,我现在将讨论这些。

易接近

HTML5 让你的网站更容易被访问。新的 HTML5 语义标签允许屏幕阅读器更容易地识别内容的类型,这允许它们为用户提供更好的体验。此外,HTML5 支持 ARIA(accessible rich Internet application)数据角色,这允许您将角色分配给内容的各个部分。这在使用 JavaScript 更新页面内容时尤其有用,因为您可以定义 ARIA 角色来监视页面的某些部分的变化并通知用户。

视频和音频支持

HTML5 对视频和音频代码都有本地支持。HTML5 视频和 HTML5 音频的主要优势之一是,它们提供强大的移动设备支持,因为它们在浏览器中工作,不需要任何像 Adobe Flash 这样的插件。目前正在讨论的一个领域是 HTML5 是否应该支持内容的数字版权管理(DRM ),并且出于防止像电影和音乐这样的数字媒体的盗版的目的,可能会将某种形式的 DRM 添加到规范中。

更智能的存储

在 HTML5 之前,客户端存储数据的主要方式是使用 cookies 这样做的缺点是,即使服务器不使用它们,它们也会随 HTTP 请求一起发送到服务器。HTML5 引入了 DOM(文档对象模型)存储,它包括 localStorage(持久的)和 sessionStorage(仅在会话期间可用)。DOM 存储的好处是数据只保留在客户端,所以它们不会影响 HTTP 请求的大小,并且它允许您存储更多的数据;目前,DOM 存储允许每个域存储 5MB 的数据。

新的互动

HTML5 的新 JavaScript APIs 使您能够添加新的和改进的交互。这方面的一个例子是新的 API,如拖放、地理定位和历史记录。这些新交互的目的是让你能够构建更丰富、更易于使用的界面。

帆布

HTML5 引入了canvas元素,这是新的 HTML5 标签,可用于绘图。它允许您构建丰富的互联网应用程序,就像过去使用 Adobe Flash 一样。

移动的

HTML5 引入了许多针对移动设备的改进,新的 API(如地理定位)允许网站确定用户的位置并提供特定位置的数据。HTML5 有一个 viewport 标签,允许你定义视窗宽度和缩放设置。还有一些设备特定的标签,使开发人员能够与浏览器特定的功能进行交互,例如,当使用 meta 标签时,开发人员可以告诉 iOS,如果在主页上添加了书签,它应该作为全屏 web 应用程序打开。

查看 HTML5 技术

既然您已经意识到了 HTML5 带来的好处,那么让我们来看看一些单独的核心变化。

文档类型

doctype 告诉浏览器应该如何解析你的文档;因此,它是文档的重要部分,应该包含在 HTML 文档的第一行。先前的 doctype 不仅将文档定义为 HTML4,还提供了规范文档的 URL,如下例所示:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd

新的 HTML5 doctype 要简单得多,您不再需要指定 HTML 的版本或规范文档的 URL,如下例所示:

<!DOCTYPE html>

变化的原因是 HTML 是一个活的规范,在标准化过程中,浏览器将继续规范的新部分。这个想法是,将来可以添加新的特性,而无需对 doctype 做进一步的更改。

新的语义 HTML 标签

当您第一次打开一个 HTML5 文档时,您首先会注意到在整个文档中使用了更多的语义标签。最值得注意的是:

<article>: Defines an article.   <aside>: Defines content alongside the main content.   <figure>: Defines related content, an example of use is photos or code listings.   <figcaption>: Defines the caption for your <figure> element.   <header>: Defines a header for the document or section.   <footer>: Defines a footer for the document or section.   <nav>: Defines a series of links used for navigation around the site.   <section>: Defines a section of content.

下面是一个 HTML5 文档布局的简单示例:

<!DOCTYPE html>

<html>

<head>

<title>Title</title>

</head>

<body>

<header>

<h1>Hello World</h1>

</header>

<div class="content">

</div>

<footer>

</footer>

</body>

</html>

新元标签

除了 HTML5 引入的新语义标签,还引入了一些新的元标签。

视口元标记

新 meta 标记中最重要的是 viewport meta 标记。这个 meta 标签最初是由 Mobile Safari 引入的,它是一种允许开发人员定义视窗宽度和缩放比例的方式。如果使用不当,viewport meta 标签会给用户带来可怕的体验。

视口元标记内容由逗号分隔的键值对列表组成,可以使用的值有:

width:– The width of the viewport.   initial-scale: The scale of the site when it initially loads.   user-scalable: By default, the user can zoom the site, setting “user-scalable” to “no” disables this. This is bad for the accessability of the site so it is discouraged.   maximum-scale: Allows you to define a maximum level that the user can zoom the site. Although not as bad as user-scalable, this can still be harmful to accessability.

如果您要将这个 meta 标记添加到一个无响应的站点,您应该将 viewport meta 标记设置为一个合理的宽度,以便舒适地显示该站点。如果您以一个 980px 的站点为例,它是居中对齐的,您可能希望在边缘周围包括一点间距,因此您可以将视区宽度设置为 1024px,如下例所示:

<meta name="viewport" content="width=1024, initial-scale=1">

对于响应式设计,您希望视口的宽度等于您正在使用的设备的宽度。这有两个主要原因:首先,您将构建以视口宽度为目标的 CSS,因此您希望视口宽度与设备宽度相匹配。第二,它告诉设备该站点是移动优化的,因此它不需要加载一个缩小的大默认视窗。

要使视窗等于您正在使用的设备的宽度,您可以将视窗宽度的值设置为device-width,而不是指定一个特定的大小。您还希望您的站点以默认的缩放级别开始,因此您将initial-scale设置为 1,如下例所示:

<meta name="viewport" content="width=device-width, initial-scale=1">

苹果触摸图标

另一个引入的新 meta 标记是 Apple touch icon meta 标记,它允许您定义当用户将网页保存到主屏幕时将在 iOS 上使用的图标,如下例所示:

<link rel="apple-apple-icon" href="apple-icon-iphone.png">

<link rel="apple-apple-icon" sizes="76x76" href="apple-icon-ipad.png">

<link rel="apple-apple-icon" sizes="120x120" href="apple-icon-iphone-retina.png">

<link rel="apple-apple-icon" sizes="152x152" href="apple-icon-ipad-retina.png">

Although not part of the HTML5 specs, these icons are necessary to allow iOS users to have a nice icon if they save the web site or web application to their home screen.

Web 表单

使用 HTML5 规范升级了输入表单字段。以前,我们仅限于单选、复选框和文本字段,但是,现在我们有了更大范围的输入类型:

search   email   url   tel   number   range   date   month   week   time   datetime   datetime-local   color

使用新输入类型的好处之一是它允许浏览器呈现相关的控件。例如,在移动设备上,如果输入类型设置为email,则会显示一个专用于插入电子邮件地址的键盘。或者,如果输入类型设置为date,则显示本地日期选择器,而不是键盘。这提供了非常好的用户体验,因为它使得表单输入更快。提供了一个为日期字段显示的自定义控件的简单示例:

<input type="date"  id="field" name="field" />

当你在手机上访问它时,在这种情况下是运行 iOS7 的苹果 iPhone,它会在浏览器中显示原生日期选取器桶(见图 1-19 )。

A978-1-4302-6695-2_1_Fig19_HTML.jpg

图 1-19。

as shown on an iPhone running iOS7

当您在桌面浏览器中加载相同的控件时,您将获得桌面的本地控件(参见图 1-20 )。

A978-1-4302-6695-2_1_Fig20_HTML.jpg

图 1-20。

The date input control shown on a desktop in Chrome

HTML5 允许您在输入字段中添加占位符;这意味着您可以为用户提供一个例子,告诉他们应该在输入字段中输入什么样的数据。向输入字段添加占位符属性的示例如下:

<input type="input" placeholder="Sample placeholder" id="field" name="field" />

当载入浏览器时,您会看到占位符的灰色文本框(参见图 1-21 )。

A978-1-4302-6695-2_1_Fig21_HTML.jpg

图 1-21。

Input element with a placeholder

在 HTML5 中,您还可以轻松地向表单添加验证。为了演示这是如何工作的,让我们看几个简单的例子。

最简单的验证形式是使字段成为必填字段,您可以通过将属性required添加到输入字段来实现这一点,如下例所示:

<input type="text" placeholder="e.g``example@example.com

当您点击浏览器中的提交按钮时,用户会看到一条错误消息,提醒他们填写该字段(见图 1-22 )。

A978-1-4302-6695-2_1_Fig22_HTML.jpg

图 1-22。

The browsers error message shown when user tries to submit the form without filling in a required value

如果您想验证一个电子邮件地址,您只需将输入类型设置为email,如下例所示:

<input type="email" placeholder="e.g``example@example.com

当您单击浏览器中的提交按钮时,用户会看到一个错误,告诉他们输入的电子邮件地址无效(参见图 1-23 )。

A978-1-4302-6695-2_1_Fig23_HTML.jpg

图 1-23。

The browsers error message shown when user tries to submit the form with an invalid email address

聚合填充

尽管 HTML5 中有这么多奇妙的新特性,但令人失望的是,我们发现并非所有这些特性都能很好地与我们需要支持的传统浏览器兼容。谢天谢地,这就是 polyfills 的用武之地。术语 polyfill 是 Remy Sharp 在 2009 年写“HTML 简介”时首次提出的。

Remy 说“Shim,对我来说,意味着你可以添加一段代码来修复某些功能,但它通常有自己的 API。我想要一种你可以放进去的东西,它会无声无息地工作。” http://remysharp.com/2010/10/08/what-is-a-polyfill/

因此,根据 Remy 的定义,polyfill 是简单地为浏览器添加缺失功能的一段代码,这通常使用 JavaScript 来实现。这个术语并不意味着暗示旧的浏览器,因为新的浏览器通常也需要填充最新的特性。

HTML5 技术已经有了大量的聚合填充,其中一些流行的有:

Respond.js: https://github.com/scottjehl/Respond Respond.js adds support for min/max-width CSS3 Media Queries to older versions of Internet Explorer (IE6-8). If you are planning on making mobile first responsive sites and need to support older IE, this is required.   HTML5 Shiv: https://github.com/aFarkas/html5shiv HTML5 Shiv enables support for styling HTML5 semantic elements in older versions of Internet Explorer.   CupCake.js: http://www.rivindu.com/p/cupcakejs.html CupCake.js adds support to both localStorage and sessionStorage with a generic API.   FlashCanvas: http://flashcanvas.net/ HTML5 Canvas Polyfill based on using Adobe Flash.

验证 HTML5 页面

您可能以前使用过 W3C 验证器来验证您的 HTML 但是,您可能不知道它已经被更新为对 HTML5 规范草案的实验性支持。

要验证您的 HTML,请访问 W3C 验证器(validator.w3.org),或者通过输入您站点的直接 URL 进行验证,或者将您站点的 HTML 粘贴到提供的文本区域。

可以使用 HTML 的验证:

As a debugging tool: The simplest bug to fix in HTML are those caused by writing invalid code. A simple validation should highlight problems with your HTML, which you can promptly fix.   To maintain quality of code: By ensuring code always passes the W3C validation, it maintains a level of quality in the code.   Ensuring ease of maintenance: Although invalid code may not break your site in the short term, unexpected bugs can crop up when you later amend that code, and validating helps minimize this.

CSS3 的新特性

随着 HTML5 带来的变化,我们也有了新的 CSS3 规范。CSS3 是 CSS 的第三个化身,它扩展了 CSS,允许我们构建更深入、更丰富的用户界面。在您开始使用响应式设计之前,了解使用 CSS3 可以实现什么是非常重要的,因为它将构成您在媒体查询中所做的许多工作的基础。

浏览器供应商前缀

在你开始看 CSS3 的例子之前,你需要了解一些关于 CSS 浏览器前缀的知识。

由于 CSS3 规范仍然是一个工作草案,浏览器供应商经常在特定于供应商的前缀后面实现新功能。这意味着它们能够实现尚未被所有浏览器完全认同的特性。每个供应商都有自己的前缀,最受欢迎的供应商的前缀是:

-moz- Firefox and browsers using Mozilla’s Gecko engine   -webkit- Safari, Chrome, and WebKit   -o- Opera   -ms- Internet Explorer

正如您将在一些示例中看到的,并非所有浏览器都需要 CSS3 属性的前缀版本,因为该属性已经开发了足够长的时间,不需要浏览器前缀,并且一些浏览器供应商从未实现过前缀版本。

在实现了 CSS 属性的前缀版本的地方,实现通常是非常相似的。然而,有一些带有前缀的 CSS 属性互不相同。线性渐变属性就是一个例子,它对每个浏览器前缀都有不同的实现。

值得注意的是,随着谷歌迁移到他们从 WebKit 中分出来的新 Blink 引擎,他们不再在浏览器前缀下添加新功能。引用他们背后的原因:

历史上,浏览器依赖于供应商前缀(例如-webkit-feature)来向 web 开发者提供实验性特征。这种方法可能对兼容性有害,因为 web 内容开始依赖这些供应商前缀名称。”8–闪烁信息页面。

这对开发者来说意味着,当 CSS3 的一些新的实验性特性进入浏览器时,我们将无法立即使用它们,因为我们网站的用户无法使用它们。这并不妨碍我们在自己的浏览器中启用实验性功能,并尝试它们,因为它们可以在 Chrome 的 about: flags 中启用。

厂商前缀 CSS 属性的完整列表可以在: http://peter.sh/experiments/vendor-prefixed-css-property-overview/ 找到。

CSS3 示例

为了真正探索你可以用 CSS3 做什么,让我们看看一些常见的元素,并讨论我们以前如何对它们进行样式化,以及 CSS3 如何使样式化变得更容易。

小跟班

网站使用按钮的目的多种多样,常见的例子有动作调用、表单提交按钮和动作按钮。从历史上来看,设计按钮的样式是相当棘手的,因为你需要使用图像,比如渐变、非网页安全字体和阴影。当我们有不同大小的按钮时,我们就需要一组不同的图像。使用 CSS3,您可以通过编写代码来实现所有这些。这不仅更简单,还意味着您可以将按钮缩放到不同的大小,而无需重新定义一般的按钮样式。清单 1-1 定义了一个渐变的按钮大小。

清单 1-1。带有渐变的单个按钮

<!DOCTYPE html>

<html>

<head>

<title>Button example</title>

<style>

.button {

display: inline-block;

border: 1px solid #1f84ef;

padding: 0px 50px;

line-height: 50px;

-webkit-border-radius: 5px;

-moz-border-radius: 5px;

border-radius: 5px;

background: #6db3f2; /* Old browsers */

background: -moz-linear-gradient(top, #6db3f2 0%, #54a3ee 50%, #3690f0 51%, #1e69de 100%);                 /* FF3.6+ */

background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6db3f2),color-stop(50%,#54a3ee), color-stop(51%,#3690f0), color-stop(100%,#1e69de)); /* Chrome,Safari4+ */

background: -webkit-linear-gradient(top, #6db3f2 0%,#54a3ee 50%,#3690f0 51%,#1e69de 100%); /* Chrome10+,Safari5.1+ */

background: -o-linear-gradient(top, #6db3f2 0%,#54a3ee 50%,#3690f0 51%,#1e69de 100%); /* Opera 11.10+ */

background: -ms-linear-gradient(top, #6db3f2 0%,#54a3ee 50%,#3690f0 51%,#1e69de 100%); /* IE10+ */

background: linear-gradient(to bottom, #6db3f2 0%,#54a3ee 50%,#3690f0 51%,#1e69de 100%);                 /* W3C */

text-transform: uppercase;

font-family: Impact, "Arial Black", sans serif;

color: #fffffe !important;

font-size: 20px;

font-size: 1.42857rem;

text-decoration: none;

-webkit-font-smoothing: antialiased;

text-shadow: 1px 0px 2px #1f84ef;

filter: dropshadow(color=#1f84ef, offx=1, offy=0);

}

.button:hover, .button:active, .button:focus {

text-decoration: none;

background: #54a7f0; /* Old browsers */

background: -moz-linear-gradient(top, #54a7f0 0%, #3c97ec 50%, #1f84ef 51%, #1c5fcc 100%); /* FF3.6+ */

background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#54a7f0), color-stop(50%,#3c97ec), color-stop(51%,#1f84ef), color-stop(100%,#1c5fcc)); /* Chrome,Safari4+ */

background: -webkit-linear-gradient(top, #54a7f0 0%,#3c97ec 50%,#1f84ef 51%,#1c5fcc 100%); /* Chrome10+,Safari5.1+ */

background: -o-linear-gradient(top, #54a7f0 0%,#3c97ec 50%,#1f84ef 51%,#1c5fcc 100%); /* Opera 11.10+ */

background: -ms-linear-gradient(top, #54a7f0 0%,#3c97ec 50%,#1f84ef 51%,#1c5fcc 100%); /* IE10+ */

background: linear-gradient(to bottom, #54a7f0 0%,#3c97ec 50%,#1f84ef 51%,#1c5fcc 100%); /* W3C */

}

</style>

</head>

<body>

<a href="#" class="button">Call to action</a>

</body>

</html>

这段代码将在浏览器中创建一个按钮,如图 1-24 所示。

A978-1-4302-6695-2_1_Fig24_HTML.jpg

图 1-24。

The finished CSS button

RGBA 吗

在 CSS 中,颜色通常被定义为十六进制或 RGB(红、绿、蓝)。如果我们想要半透明的背景,就需要使用 1 × 1px 24 位。png 然而,有了 CSS3,我们现在可以用 RGBA 颜色做到这一点。RGBA 颜色是一种应用了 alpha 透明度的 RGB 颜色。这样做的好处是,您不再需要包含图像。为了演示 RGBA,让我们在一个随机的半透明背景的图像上放置一些文本(见清单 1-2)。

清单 1-2。展示 RGBA

<!DOCTYPE html>

<html>

<head>

<title>RGBA example</title>

<style>

.rgba-container, .rgba{

width: 220px;

height: 220px;

position: relative;

}

.rgba{

background: rgba(255,255,255,0.5);

position: absolute;

top: 0px;

left: 0px;

margin: 0px;

line-height: 220px;

text-align: center;

}

</style>

</head>

<body>

<div class="rgba-container">

<img src="http://lorempixel.com/220/220/

<p class="rgba">Hello World</p>

</div>

</body>

</html>

正如您在清单中看到的,您已经将背景设置为rgba(255,255,255,0.5),它是 50%不透明度的白色。根据 RGB 值,您定义的前三个值是红色、绿色和蓝色,第四个值是颜色的不透明度,以十进制数表示。这可以在图 1-25 所示的截图中看到。

A978-1-4302-6695-2_1_Fig25_HTML.jpg

图 1-25。

The finished text over a translucent image

多列

在以前,如果你想实现多列文本,这是非常困难的。您可以选择在后端计算每列中应该出现多少个单词,或者使用 JavaScript 来设置这些列。CSS3 允许添加多个列,这意味着您可以在样式表中定义列,而不是以编程方式定义。在清单 1-3 中,您将把段落标记中的内容分成两列,每列 10px。

清单 1-3。创建两列

<!DOCTYPE html>

<html>

<head>

<title>Multiple col example</title>

<style>

p{

-moz-column-count: 2;

-moz-column-gap: 10px;

-webkit-column-count: 2;

-webkit-column-gap: 10px;

column-count: 2;

column-gap: 10px;

}

</style>

</head>

<body>

<p>Sed posuere consectetur est at lobortis. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Donec id elit non mi porta gravida at eget metus. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</p>

</body>

</html>

在这个清单中,您将column-count设置为 2,这样内容将显示为两列。然后您将column-gap定义为 10px 来分隔列。您会注意到,对于需要前缀版本的浏览器,您还包括了这些 CSS3 属性的前缀版本。你可以在图 1-26 中看到结果。

A978-1-4302-6695-2_1_Fig26_HTML.jpg

图 1-26。

The finished two columns

渐变面板

作为设计的一部分,你可能想有一个径向渐变,所以不使用图像,你可以使用 CSS3 来实现这一点。对于响应式站点来说,这样做的好处是它将随着块的宽度而缩放。参见清单 1-4 中的例子。

清单 1-4。创建径向渐变

<!DOCTYPE html>

<html>

<head>

<title>Gradient panel example</title>

<style>

.panel{

width: 220px;

height: 180px;

padding: 20px;

background: #77b7ef; /* Old browsers */

background: -moz-radial-gradient(center, ellipse cover,  #77b7ef 0%, #000000 100%); /* FF3.6+ */

background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#77b7ef), color-stop(100%,#000000)); /* Chrome,Safari4+ */

background: -webkit-radial-gradient(center, ellipse cover,  #77b7ef 0%,#000000 100%); /* Chrome10+,Safari5.1+ */

background: -o-radial-gradient(center, ellipse cover, #77b7ef 0%,#000000 100%); /* Opera 12+ */

background: -ms-radial-gradient(center, ellipse cover, #77b7ef 0%,#000000 100%); /* IE10+ */

background: radial-gradient(ellipse at center,  #77b7ef 0%,#000000 100%); /* W3C */

filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#77b7ef', endColorstr='#000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */

background-size: 160% 160%;

background-position: center -160px;

}

.panel h1{

margin-top: 0px;

}

.panel h1, .panel p, .panel a{

color: #fff;

}

</style>

</head>

<body>

<div class="panel">

<h1>Intro</h1>

<p>Cras mattis consectetur purus sit amet fermentum.</p>

<p><a href="#">read more</a></p>

</div>

</body>

</html>

你可以在图 1-27 中看到完成的径向梯度。

A978-1-4302-6695-2_1_Fig27_HTML.jpg

图 1-27。

Our finished gradient

摘要

这一章解释了响应式设计技术如何使你能够构建在各种不同设备上运行良好的网站。重要的是要记住,响应式设计不仅仅是让一个网站能够跨移动和桌面设备工作,还包括构建一个足够灵活的网站,能够跨多种设备良好运行。您无法预测未来访问您站点的设备将如何显示,因此,对于您的编程来说,最好按照大型、中型、小型和超小型来考虑设备,而不是桌面、平板和移动设备。

除了了解在哪里使用响应式设计之外,你现在应该能够识别出不适合使用响应式技术的时候。一个很好的例子是像 Google Docs 这样的桌面替代应用程序,它有很多小设备没有的功能,或者没有足够的空间展示给用户。

了解了响应式设计以及它能为你的网站提供什么之后,看看一些现有的响应式网站,了解一下使用这些技术能实现什么是个好主意。本章还看了几个正在使用的响应式设计的例子。然而,随着响应式设计的流行,有更多的好例子。AWWWARDS 网站有一个很好的不同响应网站的列表,可以在: http://www.awwwards.com/websites/responsive-design/ 找到。

让响应式设计成为可能的当然是 HTML5 和 CSS3 浏览器的改进。作为 HTML5 的一部分,新的语义标签使我们能够为我们的 HTML 带来更多的意义,这不仅有助于我们作为开发人员,而且也使屏幕阅读器能够理解页面的结构。CSS3 也带来了许多改进,让我们能够更好地控制我们的页面样式。

在下一章,我们将探索如何有效地测试我们的响应站点。我们将首先在我们的浏览器中测试我们的网站,看看我们的网站如何在调整到不同宽度的浏览器窗口中工作。然后,我们将开始研究如何在不同的设备上进行测试,从使用模拟器开始,然后研究如何在真实设备上进行测试。

Footnotes 1

伊森·马科特,名单分开。http://alistapart.com/article/responsive-web-design

2

娜塔莎·洛马斯,科技危机。http://techcrunch.com/2014/02/13/smartphones-outsell-dumb-phones-globally/

3

Joe McCann,纽约 Mother 创意技术总监,2013 年 8 月 11 日。

4

http://www.theguardian.com/technology/2013/apr/09/windows-8-touchscreen-laptops-pain

5

http://www.pcpro.co.uk/news/384172/tablet-sales-to-overtake-pcs-this-quarter

6

雅各布·尼尔森- http://www.nngroup.com/articles/repurposing-vs-optimized-design/

7

https://www.awwwards.com/websites/responsive-design/

8

闪烁信息页面 http://www.chromium.org/blink#vendor-prefixes

二、测试响应站点

在第一章中已经介绍了响应式设计以及 CSS3 和 HTML5 中的一些新特性,本章将介绍如何在你开发响应式网站时测试它们。

很可能你已经对你的站点做了一些浏览器测试,以确保你支持你的用户正在使用的浏览器。然而,当测试一个响应站点时,可能会更棘手,因为需要支持的设备范围要大得多。

你需要知道如何有效地测试你的响应站点,记住这一点,本章将解释:

How to test responsive site in the web browser   How to test on a device

在浏览器中测试响应式设计

在网站的开发生命周期中,您测试网站的第一个地方很可能就是您在网上冲浪时使用的浏览器。使用响应式设计,只要你的浏览器支持媒体查询(IE9+,Chrome,Firefox,Opera,Safari),你就可以继续这样做,当你到达需要跨浏览器测试的时候,就转向其他浏览器。

要开始测试响应站点,第一步是将响应站点的 URL 加载到浏览器中。对于这个例子,让我们看看我在 www.jonathanfielding.com 的博客(见图 2-1 )。

A978-1-4302-6695-2_2_Fig1_HTML.jpg

图 2-1。

My blog at www.jonathanfielding.com

因为这个站点是响应性的,所以可以调整浏览器的大小来测试移动视图(见图 2-2 )。

A978-1-4302-6695-2_2_Fig2_HTML.jpg

图 2-2。

My blog resized on a smaller device

这就是我的网站,在桌面和移动视图中。这种简单的调整窗口大小的过程在所有支持媒体查询的浏览器中都是一样的。

浏览器特定的测试功能

我之前提到过,你不需要改变你的浏览器来测试一个响应站点。一些浏览器提供了额外的开发工具来帮助我们测试一个响应站点。虽然您不需要为您的核心开发切换浏览器,但是了解浏览器提供的不同工具是很有用的,因此当您觉得对您的工作流有好处时,您可以切换浏览器来利用特定的工具。

谷歌 Chrome

在 Chrome 中,您可以通过以下步骤模拟一些最流行的设备:

Open the Developer tools, there are two ways in which you can do this, the first is to right-click your page and click Inspect element. The second method is to click on the menu button found to the right of the url field and select Tools ➤ Developer Tools.   Click the Show console icon to the right of the Developer Tools or press the Esc key on your keyboard. This will open the Console drawer, as shown in Figure 2-3.  With the Console drawer open, you can now select the Emulation tab. As you can see in Figure 2-4, the Device line indicates it will default to Google Nexus 4.

A978-1-4302-6695-2_2_Fig3_HTML.jpg

图 2-3。

The Console drawer open in Chrome’s InspectorUsing the drop-down menu, you have the option to emulate a specific device. Select the Apple iPhone 4 and click Emulate (see Figure 2-5).

A978-1-4302-6695-2_2_Fig4_HTML.jpg

图 2-4。

The Emulation tab, preselected with a default deviceHaving selected the device to emulate, you will see the viewport has now automatically narrowed, and when you move your mouse cursor over the viewport, it will turn into a circle to signify your finger (see Figure 2-6).

A978-1-4302-6695-2_2_Fig5_HTML.jpg

图 2-5。

Selecting the iPhone in the Emulation tab

A978-1-4302-6695-2_2_Fig6_HTML.jpg

图 2-6。

With a device selected and emulation enabled, the browsers viewport changes Note

Chrome 中的模拟并不是真正的设备模拟,而是简单地模拟设备的一些关键特性。这些包括视窗大小、设备像素比率、用户代理和触摸事件。这意味着任何特定于浏览器的错误都不会显示出来,因为你仍然在 Chrome 中测试。

火狐浏览器

Firefox 对此采取了一种稍微不同的方法。您可以进入响应式设计模式,而不是模拟特定设备,该模式允许您测试常见的视口宽度。

Open the developer tools. The easiest way to do this is to right-click the web page and click Inspect element.   Enter responsive design view; this is achieved using the icon tab on the far right.   Now that you are in responsive design view, you can see the site in a smaller viewport. You have the option to switch to landscape view, enable touch events, or change the viewport size to other commonly used viewport sizes. You can also resize the viewport and then save the new viewport size as a preset. You can see the Firefox interface in Figure 2-7.

A978-1-4302-6695-2_2_Fig7_HTML.jpg

图 2-7。

The Firefox inspector with the Responsive Design view active

在设备模拟器上测试

到目前为止,您一直在通过调整浏览器窗口大小或使用浏览器内置的工具来测试响应性站点。虽然这对于查看媒体查询是如何工作的是有效的,但是它不能提供它们将如何影响移动设备上的站点的全貌。我们现在将看看如何在设备模拟器和实际设备上测试我们的项目。

在开始在真正的移动浏览器上进行测试之前,您需要确定您将支持哪些浏览器。根据网站的目标受众,这可能因网站而异,所以如果你已经对用户使用的浏览器进行了一些分析,这是一个好的开始。此外,如果您的网站针对某个特定的国家,通过查看 StatCounter GlobalStats 网站( http://gs.statcounter.com/ ),很容易找到使用每个平台的用户百分比的统计数据。

目前有五种主要的智能手机操作系统:

iOS   Android   Windows Mobile   BlackBerry OS   Firefox OS

决定了要在哪些浏览器上进行测试之后,让我们看看如何在真正的移动浏览器上进行测试。有两种选择:第一种是使用模拟器,第二种是使用一些实际的设备开始测试。

模拟器

模拟器为测试您的响应站点提供了一个极好的开端。它们能够在您的本地机器上运行,通常能够访问您所有的本地文件和您所在网络上的文件。

iOS 模拟器

如果你使用的是 Mac,你可以从苹果应用商店下载 Mac 开发者工具包 Xcode。其中包括 iOS 模拟器,可用于测试响应站点。要开始使用 iOS 模拟器,您需要遵循以下步骤:

Download Xcode from the Apple App Store.   Open Xcode.   Use the menu bar to navigate to Xcode ➤ Open Developer Tool ➤ iOS Simulator.   Open Safari by clicking the Safari icon in the dock.

现在,您可以访问任何网站,包括存储在 Hosts 文件中的本地网站。如果你只是想测试一个 HTML 文件,你甚至可以将它拖放到 iOS 模拟器窗口中。

不幸的是,由于苹果尚未推出与操作系统兼容的 iOS 模拟器版本,因此无法在微软 Windows 上测试移动 Safari 中的网站。

机器人

Android 模拟器作为 Android SDK(软件开发工具包)的一部分在 Windows 和 Mac 上都可用。

苹果个人计算机

Download the Android SDK from http://developer.android.com/sdk .   Extract the zip file to ∼/bin/Android (∼is the Unix shorthand for your user directory, so if your username is Jonathan, the full folder path would be /Users/jonathan/bin/Android).   Open your terminal.   Navigate to the SDK, which is located at ∼/bin/Android/sdk/tools. You can navigate to this path using the change directory Unix command cd, so the full command you enter into your terminal would be ∼/bin/Android/sdk/tools.   Run the SDK Manager by running the ./android command in your terminal. This will load another application (be aware this application may take a while to load and unfortunately does not give any indication of the status of it loading).

Windows 操作系统

Download Android SDK from http://developer.android.com/sdk .   Extract the zip file to C:\Program Files\Android.   Launch the SDK Manager.exe.

共享步骤

You will now be able to select the SDK version you want to test. In this case I have selected Android 4.3. Then simply click Install, reading and accepting any licenses as required.   You then need to select Tools ➤ Manage AVDs on the menu bar. Then click the New button to create a new Android Virtual Device.   You will now need to enter the settings for your Simulator (see Figure 2-8).  You can now select your simulator and click Start (see Figure 2-9).

A978-1-4302-6695-2_2_Fig8_HTML.jpg

图 2-8。

The create a Simulator prompt allows you to configure your simulatorWith the Android emulator installed, you can now simply use the emulator as you would with a normal Android device (see Figure 2-10).

A978-1-4302-6695-2_2_Fig9_HTML.jpg

图 2-9。

Android Virtual Device Manager lists the available Simulators

A978-1-4302-6695-2_2_Fig10_HTML.jpg

图 2-10。

The Android Simulator, with the hardware buttons included in the window

Firefox OS 模拟器

Firefox OS Simulator 作为 Firefox 的附加软件运行,从 Firefox 26.0 开始,它的安装非常简单。

Launch Firefox and open the URL about:app-manager. You will be presented with the built-in App Manager, as shown in Figure 2-11.  You can now click Start Simulator. Because there is no simulator installed, you will be given the option to install the simulator, so click Install Simulator to continue, as shown in Figure 2-12.

A978-1-4302-6695-2_2_Fig11_HTML.jpg

图 2-11。

The Firefox App Manager tool can be used to launch a Firefox OS SimulatorYou will now be taken to a web page to install a simulator, simply follow the instructions to install the latest version.   Once installed, go back to about:app-manager and refresh the page. Click Start Simulator and then select the version of the simulator you wish to start. If it still says Install Simulator, you should try to install a different version (see Figure 2-13).

A978-1-4302-6695-2_2_Fig12_HTML.jpg

图 2-12。

The App Manager allows you to install the simulator if it is not already installedAfter a short loading time, you will find yourself at the Firefox OS home screen, where you can select the web browser. The OS Simulator is shown in Figure 2-14.

A978-1-4302-6695-2_2_Fig13_HTML.jpg

图 2-13。

The option to choose from different versions of the Simulator to installUpon loading the browser, you can enter your web site URL and start testing your site. The browser is shown in Figure 2-15.

A978-1-4302-6695-2_2_Fig14_HTML.jpg

图 2-14。

Firefox OS Simulator home screen, with the browser found in the bottom right-hand corner

A978-1-4302-6695-2_2_Fig15_HTML.jpg

图 2-15。

Firefox running on the Firefox OS Simulator

黑莓操作系统模拟器

BlackBerry Limited 为 BlackBerry OS 提供了可以在 Windows 和 Mac 上运行的模拟器,它们安装起来非常简单。但是,如果您还没有安装这些应用程序,您还需要在 Windows 上安装 VMware Player 或在 Mac 上安装 VMware Fusion,这些应用程序可以在 www.vmware.com 找到。

Download the emulator at http://uk.blackberry.com/sites/developers/resources/simulators.html .   Run the Installer.   Run the Virtual Machine in VMware, as shown in Figure 2-16.

A978-1-4302-6695-2_2_Fig16_HTML.jpg

图 2-16。

BlackBerry simulator running in VMware

Windows Phone 模拟器

Windows Phone 模拟器是 Windows Phone SDK 的一部分,目前仅适用于 Windows 8+。不幸的是,运行 Windows Phone 模拟器也有特定的硬件要求,可以在msdn.microsoft.com/en-us/library/windowsphone/develop/ff626524(v=vs.105).aspx找到。

Download the Windows Phone SDK from http://dev.windowsphone.com/en-us/downloadsdk .   Run the SDK installer (this requires 6.5GB of hard drive space).   Upon completion of the installation, you will be told you need a license for Visual Studio, so you can either cancel and use the trial or register for a free license at http://www.visualstudio.com/en-US/products/visual-studio-express-vs .   Check for available updates to the SDK.   Open the command line (cmd) by right-clicking and clicking Run as administrator.   Run the command:

"C:\Program Files (x86)\Microsoft XDE\8.0\XDE.exe" /vhd "C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Emulation\Images\Flash.vhd" /name WP8SDK

With the emulator now open, you should now be able to run Internet Explorer in the Simulator, as shown in Figure 2-17.  To make opening the simulator easier in the future, create a .bat file with the command.

A978-1-4302-6695-2_2_Fig17_HTML.jpg

图 2-17。

Internet Explorer running on Windows Phone Simulator

物理设备

到目前为止,我一直关注如何通过在浏览器中测试站点和使用电话模拟器来测试开发机器上的站点。尽管用这些方法测试一个站点非常实用,并且可以让你快速地进行初始测试,但是它们并不能真实地反映出这个站点在真实设备上的运行情况。

其中一个原因是,在开发机器上,您只能使用键盘和鼠标模拟设备使用的不同输入方法。通过在真实设备上进行测试,您可以使用用户会使用的输入法进行测试。如果不在物理设备上测试网站,您可能不会注意到可用性方面的问题。您可能没有注意到的常见问题是按钮太小而无法点击,或者文本在小显示屏上不可读。

考虑到这一点,在尽可能多的设备上测试响应站点是非常重要的。然而,如果你刚刚开始收集设备,选择购买哪些设备可能会非常困难,尤其是在你预算有限的情况下。在这种情况下,了解一下用户正在使用的设备是很有意义的,在选择您需要的设备时,请确保您考虑周全。设备应该至少覆盖最流行的操作系统,如果可能的话,混合高端和低端。

在设备上调试网站

当在各种不同的设备上测试网站时,您需要能够调试用户可能面临的任何问题。进行调试的方式取决于设备的操作系统。

在 iOS 上调试站点

随着 iOS6 的发布,苹果为 iOS 引入了远程调试功能,使开发人员能够将 Mac Safari Web Inspector 连接到移动 Safari 中打开的网站。若要启用此功能,您需要打开 iOS 设置➤ Safari 设置➤高级设置并打开 Web 检查器。在图 2-18 中,您可以看到 Web Inspector 设置已启用。

A978-1-4302-6695-2_2_Fig18_HTML.jpg

图 2-18。

Advanced settings for iOS Safari with Web Inspector enabled

您还需要确保在 Mac Safari 上启用了“开发”菜单项。要做到这一点,只需打开 Safari 并检查“开发”项目是否出现在顶部菜单上。如果看不到,您需要打开“高级”标签下的 Safari 偏好设置。要启用开发菜单,只需勾选菜单栏中的显示开发菜单复选框,如图 2-19 所示。

A978-1-4302-6695-2_2_Fig19_HTML.jpg

图 2-19。

Safari preferences with Develop menu enabled

现在,Mac Safari 和 iOS 设备都已设置好,您可以开始将两者连接在一起,以便调试您的站点。为此,首先使用 USB 电缆将您的设备插入电脑。完成此操作后,您可以在 Mobile Safari 中打开您的站点。要在 Mac 上打开检查器,只需打开显示设备的开发菜单。选择您的设备后,您将看到一个页面列表,您可以选择调试,如图 2-20 所示。

A978-1-4302-6695-2_2_Fig20_HTML.jpg

图 2-20。

The Develop menu in Safari

选择要调试的站点后,Safari 检查器面板将会打开,您可以完全访问站点代码。从这里开始调试就像在桌面 Safari 上调试一样,突出显示源代码中的一个元素将会突出显示 iOS 设备上的相应元素。Safari 检查器面板如图 2-21 所示。

A978-1-4302-6695-2_2_Fig21_HTML.jpg

图 2-21。

Safari Inspector panel

在 Android 上调试网站

与 iOS 类似,你可以从你的开发机器上调试运行在 Android 上的 Chrome 中的网站。

第一步是配置您的 Android 设备,使其允许远程调试。您的操作方式取决于您的设备运行的 Android 版本。

对于 Android 4.2 或更高版本,进入设置➤关于手机,并点击建立号码七次(听起来有点像旧的 Konami 代码!),然后返回上一个屏幕。然后导航到新菜单选项 Developer options 出现的前一个屏幕。在调试下,您现在可以启用 USB 调试。该设置在图 2-22 中突出显示。

对于 Android 4.0 或 4.1,进入设置➤开发者选项。在调试下,您现在可以启用 USB 调试。该设置在图 2-22 中突出显示。

A978-1-4302-6695-2_2_Fig22_HTML.jpg

图 2-22。

The Android Developer options with USB debugging enabled

如果您使用的是 Windows,在这个阶段您需要安装 USB 设备驱动程序来连接 Android 设备。这些都可以在 http://developer.android.com/tools/extras/oem-usb.html 下载。

此时,您需要更改 Chrome 中的设置来启用远程调试支持。为此,请在您的网络浏览器中输入 URL about:inspect。这将显示 Chrome 开发工具的设置页面。然后,您可以通过选中“发现 USB 设备”复选框来启用设备发现,如图 2-23 所示。

A978-1-4302-6695-2_2_Fig23_HTML.jpg

图 2-23。

Enable device discovery in Chrome

您现在处于可以通过 USB 连接设备的阶段。连接时,您可能会在设备上看到请求 USB 调试权限的警告。为了避免将来出现这种情况,您可以在单击“确定”之前选中“总是允许来自这台计算机”复选框。

你现在可以在你的设备上用 Chrome 打开你的网站了。这将出现在您插入电脑的设备下方的chrome://inspect页面上。如图 2-24 所示。

A978-1-4302-6695-2_2_Fig24_HTML.jpg

图 2-24。

Choose a tab on your device you want to inspect

在您的设备上加载一个页面后,您现在可以单击站点名称下方的 Inspect 链接,这将打开检查器。这是你用来检查你桌面上的网站的普通 Chrome 检查器(如图 2-25 所示)。此外,您安装在浏览器上的所有开发工具插件也可以用来调试您的站点。

A978-1-4302-6695-2_2_Fig25_HTML.jpg

图 2-25。

Chrome Developer Tools inspecting the site on the Android device

开放设备实验室

如果你没有预算购买任何自己的设备,你可以看看在你工作或居住的地方附近是否有任何开放的设备实验室( http://opendevicelab.com/ )。开放设备实验室是一项社区运动,旨在建立任何人都可以去测试的设备池。他们通常有一系列不同的设备,这将使您能够很好地了解您的网站如何在这些设备上工作。

在线解决方案

您可能会遇到这样的情况:您无法接触到设备,并且不住在开放的设备实验室附近。在这种情况下,也有在线解决方案,允许您跨各种设备进行测试。

第一个在线解决方案是 Perfecto Mobile ( www.perfectomobile.com/)。Perfecto Mobile 不是一个模拟器,而是你可以远程控制真实的设备。这意味着你可以更真实地了解用户在使用你的网站时的感受。

第二个解决方案是 BrowserStack ( www.browserstack.com),它允许你在大量不同的浏览器上测试你的网站。除了允许你测试和调试你的响应站点,BrowserStack 还允许你在不同的浏览器和设备上快速生成你的手机截图。值得一提的是,使用 BrowserStack 并不需要公开您的站点,因为它会安装一个浏览器插件,将您的本地站点安全地代理到它们的服务器。

摘要

测试网站对于构建可在各种不同设备上运行的网站至关重要。因此,确保你有一个关于如何测试你的站点的策略是非常重要的。

在计划测试网站的策略时,你应该确保考虑到用户使用的浏览器和设备。

本章探讨了如何在这些不同的平台上进行测试,包括最初如何在浏览器中进行测试,然后在真实设备或模拟器上进行测试。您的策略应该将两者都考虑在内,因为如果您只在桌面浏览器上进行过测试,就不能简单地指望网站在真实设备上运行。像 Perfecto Mobile 和 BrowserStack 这样的第三方工具可能是这个策略中很有价值的部分,因为它们可以让你更好地访问额外的浏览器进行测试。

在 web 开发的大背景下,你需要确保你有足够的时间来测试你的站点。测试不是你可以在过程的最后简单地做的事情,而是应该成为你工作的一个组成部分,你应该能够在实现的时候测试一个特定的特性。

彻底的测试使你能够确保不会遗漏任何东西,并且你已经看到了用户从不同设备获得的不同体验。

在下一章,我们将探索媒体查询的力量,以及它们对响应式设计的重要性。你不仅会对什么是媒体查询有一个全面的了解,还会了解它们在构建一个能够响应用户浏览器环境的站点时所给予你的灵活性。

三、媒体查询的力量

第一章介绍了 CSS3,但有一个明显的遗漏:没有讨论媒体查询。而且理由很充分;媒体查询是响应开发的一个如此大的领域,它们应该有自己的一章。

媒体查询是响应式设计的核心,因为它们允许开发人员根据设备的功能来确定特定的 CSS 样式。最常见的目标功能是视口宽度;然而,媒体查询远不止这些,我将在本章中解释这些功能。

本章将涵盖:

  • 媒体查询简介
  • 在 CSS 中使用媒体查询
  • 移动优先与桌面优先
  • 瞄准高像素密度显示器

媒体查询简介

媒体查询是响应式设计的基础,允许您根据指定的查询来更改网站的外观。媒体查询可以分成两种类型的部分:媒体类型和媒体表达式。

为了开始对媒体查询的介绍,让我们看看媒体类型,它是作为 CSS2.1 规范的一部分引入的。

媒体类型

在新的 CSS3 草案规范之前,CSS2.1 引入了媒体类型,允许开发人员添加针对不同类型设备的媒体相关样式表。总共有十种不同的媒体类型,其中三种你可能已经遇到过:全媒体、屏幕媒体和印刷媒体。

如果您在过去遇到过媒体类型,很可能您已经看到过它们被用来根据设备类型启用或禁用样式表。这方面的一个例子是:

<link rel="stylesheet" type="text/css" href="all.css"    media="all" />

<link rel="stylesheet" type="text/css" href="screen.css" media="screen" />

<link rel="stylesheet" type="text/css" href="print.css"  media="print" />

除了屏幕和打印样式表,还有许多其他媒体类型支持各种其他设备:

all: all devices   aural: speech synthesizers   braille: braille tactile feedback devices   embossed: paged braille printers   handheld: small or handheld devices   print: printers   projection: projected presentations, like projectors and projected slides   screen: computer screens   tty: media using a fixed-pitch character grid, like teletypes and terminals   tv: television-type devices

除了能够为每种媒体类型指定不同的样式表之外,还可以使用以下语法将这些样式包含在您的样式表中:

@media print {

/* print styles go here */

}

在主样式表中包含不同的媒体类型查询有几个好处:

It reduces the number of HTTP requests. An increase in the number of HTTP requests can lead to reduced performance of loading a site.   Separate style sheets can increase the time that the page is blocked from rendering, as most browsers will wait until all the individual style sheets are downloaded before they will render the content.

Stoyan Stefanov 的一项实验发现,不仅浏览器下载打印样式表,大多数浏览器还在呈现之前等待它下载(即使并不严格要求向用户显示页面)。这包括用户不太可能打印的移动浏览器。 1 因此,这里的好处是通过减少浏览器必须加载的样式表数量来减少这种阻塞。

当应用媒体类型时,考虑浏览器在任何给定的时间将只声明一种媒体类型。这意味着,如果浏览器实现了手持媒体类型,那么您的屏幕媒体类型的样式将不会被应用,尽管假设手持设备具有屏幕。

对于媒体类型,您依赖于浏览器来实现您期望的设备媒体类型。浏览器供应商并不总是像预期的那样实现媒体类型的一个很好的例子是电视上的浏览器。尽管有些浏览器可以正确支持电视媒体类型(包括 Opera),但有些电视浏览器会错误地同时应用电视和屏幕媒体类型。这意味着,如果您使用一种屏幕媒体类型,将特定于您的桌面体验的样式作为目标,它们可能会意外地应用到电视上,这根据规范是不正确的:

A user agent can only support one media type when rendering documents. 2

媒体查询

CSS3 中添加了媒体查询作为媒体类型的扩展,目的是让开发人员能够更好地控制他们的网站在不同浏览器和设备上的显示方式。其思想是,不必为不同的设备构建和维护每个页面的多个版本,而是可以根据设备的属性、特性或特征在 CSS 中修改单个网站。

与简单地告诉您设备类型的媒体类型不同,媒体查询为 CSS 添加了一个逻辑层次,即如果满足某个条件,则应该应用样式,否则应该忽略它们。这意味着,您现在可以针对设备的单个特征,而不是简单地针对设备的类型。

媒体查询可以测试什么?

媒体查询可以测试各种不同类型的查询。这些将在接下来的章节中讨论。

宽度|最小宽度|最大宽度

width查询允许您测试浏览器视窗的宽度。这使您能够以特定的浏览器宽度为目标样式。您不仅可以根据设定的宽度进行测试,还可以将浏览器视窗的最小宽度或最大宽度作为目标。这意味着您可以使用该查询来匹配各种不同的设备宽度。width媒体表达是最常用的媒体表达之一,用于调整站点以做出响应。

高度|最小高度|最大高度

height查询允许您测试浏览器视窗的高度。类似于width查询,您可以确定精确的高度、最小高度或最大高度。虽然height查询比width查询用得少,但是height查询在您希望确保特定内容在页面首次加载时对您的用户可见(或不可见)的情况下特别有用,因为您可以使用它来调整内容的高度,使其最适合视窗的高度。

设备宽度|最小设备宽度|最大设备宽度

device-width查询允许您测试设备的宽度。您可以设定精确宽度、最小宽度或最大宽度。widthdevice-width的区别在于,宽度与浏览器的width有关,而device-width与设备屏幕的宽度有关。虽然有一些使用device-width的用例,但问题是如果用户在桌面上调整浏览器的大小,网站不会调整到合适的大小。此外,如果您正在使用 viewport meta 标签并将width设置为等于device-width,您应该只使用width查询。

设备高度|最小设备高度|最大设备高度

device-height查询允许您测试设备的高度。您可以设定精确的高度、最小高度或最大高度。device-heightheight的区别在于device-height与设备屏幕的高度有关,而height与视窗的高度有关。这种区别在能够调整浏览器窗口大小的设备上非常重要。

纵横比|最小纵横比|最大纵横比

aspect-ratio查询允许您测试设备视窗的纵横比。器件的纵横比是器件较长边的长度与器件较短边的长度之比。当您想要将资产定位为匹配设备的宽高比时,包括显示视频,这是对用户设备的最佳优化,那么aspect-ratio查询可能特别有用。

设备纵横比|最小设备纵横比|最大设备纵横比

device-aspect-ratio查询允许您测试设备的纵横比。device-aspect-ratioaspect-ratio的区别在于device-aspect-ratio与设备屏幕的纵横比有关,而aspect-ratio与视窗的纵横比有关。这种区别在能够调整浏览器窗口大小的设备上很重要,因为aspect-ratio和视窗大小一样灵活,而device-aspect-ratio的值不会改变。

颜色|最小颜色|最大颜色

color query允许您根据每个颜色组件的位数来测试设备的颜色功能。

颜色指数|最小颜色指数|最大颜色指数

color-index查询允许您测试设备支持的颜色数量,该值必须是整数,不能是负数。

单色|最小单色|最大单色

monochrome查询允许您在单色设备上测试每像素位数,使用1表示真,0表示假。

分辨率|最小分辨率|最大分辨率

resolution查询允许您测试设备的像素密度。分辨率查询接受三种不同类型的值:dpi(每 CSS 英寸点数)dpcm(每 CSS 厘米点数)和dppx(每像素点数)。首选是使用dppx,这是比dpcmdpi更新的规范。dppx 相对于其前辈的优势在于,它与屏幕的像素密度直接相关,因此开发人员更容易理解。

扫描

scan查询允许您测试设备的扫描过程。这是电视机特有的,它可以进行逐行或隔行扫描。这两者的区别在于,逐行显示一次在显示器上画出所有的线,而隔行显示画出所有的奇数线,然后画出偶数线,以欺骗眼睛认为他们一次看到了所有的线。

格子

grid查询允许您测试设备是网格设备还是位图设备,有两个可能的值。如果该值被设置为 1,那么如果设备的显示是基于网格的,查询将启用 CSS,例如只有一种固定字体的电话显示。或者,您可以通过将该值设置为 0 来检查所有其他设备。

方向

orientation查询允许您测试设备是横向的还是纵向的,并适当地应用您的 CSS。orientation查询的一个典型用例是,您可能希望在纵向的一列和横向的两列之间切换。

Note

您可以使用的查询类型取决于设备的媒体类型。并非所有媒体类型都支持所有查询的原因是,这并不总是有意义的,例如,如果用户使用听觉设备,则涉及视口或屏幕的查询(如宽度和设备宽度)将没有意义。

媒体查询的语法

既然您已经熟悉了媒体类型和媒体查询及其用途,那么让我们来探索编写它们的方法。

媒体查询至少由媒体类型组成,并且可以另外具有一个或多个媒体表达式,其返回 true 或 false。对于要应用的 CSS,媒体类型应该与加载页面的设备相匹配,并且所有媒体表达式必须返回 true。媒体查询可以根据您的需要具体或模糊,从而确保您的 CSS 完全按照您期望的方式应用。查看编写媒体查询的语法的最佳方式是深入研究一些示例。

第一个例子展示了如何将 CSS 添加到具有小视窗的设备中;通常是移动电话。如果您只想在小视窗上启用 CSS,那么您需要添加一个规则来定义 CSS 将应用到的视窗的宽度上限。在这种情况下,您可以添加一个max-width规则,并将值设置为767px。一个简单的例子是:A978-1-4302-6695-2_3_Figa_HTML.jpg

在本例中,媒体类型被设置为all,这意味着它适用于所有媒体类型。然后有一个 max-width 设置为767px的媒体表达式,因为您希望将目标指向额外的小设备(例如,移动电话),在这种情况下,它被定义为小于767px。这里的理由是,许多可能被归类为小型设备的设备,如 iPad,其最小宽度为768px。这个媒体查询背后的逻辑是,对于所有设备,如果视口的宽度小于或等于767px,则应用 CSS 样式。

如果您只想将 CSS 应用于屏幕设备,您可以将媒体查询中使用的媒体类型更改为 screen。这一变化反映在这个更新的例子中:A978-1-4302-6695-2_3_Figb_HTML.jpg

这个媒体查询的逻辑类似于上一个例子,但是,关键的区别是您已经将媒体类型更改为screen。因此,逻辑是,对于类型屏幕的所有设备,如果视口的宽度小于或等于767px,则 CSS 是活动的。

“非”逻辑运算符

如果您想将 CSS 应用于除屏幕之外的所有媒体类型,您可以利用not逻辑运算符。媒体查询中的 not 逻辑运算符告诉浏览器反转表达式的结果。对于屏幕示例,您可以简单地将 not 逻辑运算符添加到表达式的开头:A978-1-4302-6695-2_3_Figc_HTML.jpg

所以对于这个例子,逻辑是如果浏览器不是宽度小于等于767px的屏幕设备,那么样式表就会被激活。

“唯一”逻辑运算符

only逻辑运算符用于防止不支持媒体表达式的旧浏览器试图处理媒体查询。如果没有only操作符,旧的浏览器将读取媒体类型,但是,它将不能理解媒体表达式;因此,媒体表达式被忽略,样式被应用。

出现这种现象的原因是,在用于媒体查询之前,媒体属性最初用于媒体类型。尽管 CSS 规范已经扩展到包括 CSS3 中的媒体查询,但是您仍然需要支持使用旧规范的旧浏览器。使用only逻辑运算符的媒体查询只是在媒体类型运算符前加上。此示例更新了先前用于测试超小型设备的媒体查询:A978-1-4302-6695-2_3_Figd_HTML.jpg

本例中媒体查询的逻辑与之前完全相同,但是,您现在添加了only逻辑运算符,这将防止此媒体查询中包装的样式在较旧的浏览器中被错误地应用。

使用多个表达式

媒体查询真正强大的原因之一是能够一起使用多种媒体表达式。这意味着,如果你想针对小型设备,如平板电脑,而不影响额外的小型设备,你可以这样做,使用两种媒体表达方式,而不是一种。

要链接表达式,您需要在媒体表达式之间使用and关键字。本例使用最小宽度768px和最大宽度1023px来确定设备是否为小型设备:A978-1-4302-6695-2_3_Fige_HTML.jpg

这个媒体查询的逻辑是,如果媒体类型是 screen,viewport 等于或大于768px,并且小于或等于1023px,那么样式表将被激活。

链接媒体查询

到目前为止,示例只是使用了单独的媒体查询;但是,通过链接媒体查询并允许在多种情况下应用 CSS,您可以在任何查询返回 true 时应用 CSS。每个媒体查询可以具有其自己的媒体类型和其自己的媒体表达,这意味着单独的媒体查询可以针对不同的媒体特征、类型和状态。要使用多个媒体查询,只需在每个查询之间添加一个逗号来分隔它们,然后将对每个查询进行单独评估,以查看它们是否为真,如下例所示:A978-1-4302-6695-2_3_Figf_HTML.jpg

在本例中,有两个媒体查询。如果浏览器的宽度等于或大于768px并且小于或等于1023px,则第一个媒体查询将为真。如果设备的方向是纵向,则第二媒体查询将为真。现在,如果这两个媒体查询中的任何一个为真,那么样式将被激活。

您可能希望使用多个媒体查询的一个常见示例是针对在不同浏览器中以不同方式实现的功能。例如,当您使用最小分辨率表达式时,如以下代码所示:

@media only screen and ( -webkit-min-device-pixel-ratio: 2 ),

only screen and ( -o-min-device-pixel-ratio: 2/1 ),

only screen and ( min--moz-device-pixel-ratio: 2 ),

only screen and ( min-device-pixel-ratio: 2 ),

only screen and ( min-resolution: 192dpi ),

only screen and ( min-resolution: 2dppx ) {

/* High resolution styles here */

}

在 CSS3 媒体查询的早期,需要实现一种针对高像素密度设备的方法,这导致 WebKit 在其供应商前缀下实现device-pixel-ratio表达式。随着规范的成熟,正确的实现是使用“resolution”表达式;然而,为了支持这些旧的浏览器,您需要使用多个媒体查询。

在 CSS 中使用媒体查询

既然您已经探索了编写媒体查询的语法,那么是时候看看如何将它们应用到页面上了。这三种方法是:

Separate style sheets for each media query   Use @import in the main CSS file to load CSS files conditionally   Use the media queries inside CSS file

单独的样式表

通常,在过去使用屏幕或打印样式表时,每种样式表都有一个样式表,其中包含适用于哪种媒体类型的规则。类似地,您可以通过媒体查询来有条件地启用和禁用样式表。为此,您可以在用于包含样式表的链接标签上使用 media 属性。这个属性与您之前在样式表中添加媒体类型规则时使用的属性相同。添加规则时,只需将其放入“媒体”属性,如下例所示:

<link rel="stylesheet" media="screen and (min-width: 768px) and (max-width: 1023px)

" href="tablet.css" />

在这个例子中,媒体查询用于检查用户是否在小型设备上,并且这被简单地添加到媒体属性中。

使用单独的样式表的好处是,它允许您更容易地划分代码。请记住,媒体查询中的所有样式表都被下载到用户的设备上,如果不需要,它们就不会被激活。这意味着,通过使用单独的样式表,可以增加向服务器发出的 HTTP 请求的数量。

使用@import

如果您想为每个媒体查询使用单独的样式表,但不想在 HTML 中定义它们,您可以使用 CSS @import 语法并应用媒体查询。@import 语法可以分为三个部分:

The @import declaration   The URL to CSS file to include   One or more media queries

将这些放在一起,您最终会得到以下结果:

@import url("tablet.css") screen and (min-width: 768px) and (max-width: 1023px);

在本例中,您使用媒体查询来检查用户是否在小型设备上,并简单地将其添加到用于加载tablet.css文件的@import 语法中。

类似于在标签中使用链接到页面的单独样式表,通过@import 加载的样式表增加了 HTTP 请求的数量。然而,除此之外,@import 可以防止样式表被同时下载。

在 CSS 中使用媒体查询

您可以在站点的样式表中定义媒体查询,而不是将 CSS 分成单独的样式表。这允许您定义新的样式,并在条件为真时对现有样式应用覆盖,从而利用 CSS 的级联特性。

要在 CSS 文档中编写媒体查询,可以使用@media 语法。如何使用该语法的分类如下:

The @media declaration   One or more media expressions

因此,@media语法的一个例子是:

@media only screen and (min-width: 768px) and (max-width: 1023px){

/* Your styles. */

}

尽管这只是一个简单的例子,但它向您展示了在 CSS 文件中包含媒体查询是多么容易。与添加到您的<head>中的@import和单独的文件不同,在主 CSS 文档中使用媒体查询与在主 CSS 文档中包含您的媒体类型具有相同的好处,这些好处是减少 HTTP 请求和减少阻塞。

移动优先与桌面优先

开发人员通常会以他们最舒服的方式编码——毕竟,人类是习惯性的动物,不管有些人怎么认为,开发人员也是人。因此,当响应式 web 设计出现时,最常见的方法是首先构建桌面站点,然后使用媒体查询使其适应移动应用程序;毕竟,他们的大部分用户仍然来自于桌面应用,开发人员已经习惯了为桌面开发。

这种方法被称为适度降级,其思想是,你应该首先创造尽可能好的体验,然后开始考虑浏览器功能下降时的降级。对于一个响应式网站来说,这意味着当视窗缩小时,网站会缩小,删除内容和功能。

然而,还有一种更适合响应式设计的方法,叫做渐进增强。渐进增强并不是一个新的想法;它是由 Steven Champeon 在 2003 年奥斯汀的西南偏南互动会议上首次提出的。在为 Webmonkey 写的后续文章中,Steven 说:

Don't try to mix the representation into the tag that the lowest common denominator browser can't handle anyway, but strip it out. Make sure that only competent browsers will request it first. The more we know about what browsers support, the better it will be for us to set tags and styles. 3

Steven 所说的是用最少的功能为浏览器建立你的网站,然后通过增加新的功能和内容逐步改进网站。如果您想将这种方法应用到一个响应式站点,您应该首先构建移动站点。然后,随着设备性能的提高,您可以增强这些站点。这种渐进式改进被称为移动优先响应设计。

当把优雅的退化与渐进的增强相比较时,乍一看,它们只是一枚硬币的两面。对于适度降级,我们只需从构建最好的网站开始,然后降低体验;对于渐进增强,我们从构建功能最差的浏览器的体验开始,然后渐进增强桌面的体验。

事实上,当我们首先构建桌面网站时,我们通常会在网站上添加许多功能。这些可能为桌面用户提供了很好的用户体验,但是,通常有一些功能不能很好地扩展。接下来的问题是如何让网站的这一部分在移动应用上工作。这正是卢克·乌鲁布莱夫斯基 2009 年在他的博客上提出的观点:

Mobile devices require the software development team to focus only on the most important data and actions in the application. There is no room for irrelevant and unnecessary elements on a 320×480 pixel screen. You must prioritize. Therefore, when a team first designs a mobile device, the end result is an experience of focusing on the key tasks that users want to complete, without unnecessary detours and interface fragments that can be seen everywhere in today's desktop access websites. This is a good user experience, which is also good for enterprises. 4

他的意思是,通过首先关注移动设备,我们可以为用户提供更好的体验。我们不会让不必要的功能塞满我们的界面,因为我们必须优先考虑对用户重要的功能。通过扩展浏览器的功能,移动应用还使我们能够添加额外的功能,例如,精确的地理位置信息和触摸事件。这意味着,虽然移动浏览器在某些方面可能有局限性,但它们提供了我们可以轻松利用的额外功能。

当我们对我们的移动构建感到满意时,我们就可以继续扩展视口并逐步增强站点,使用诸如特征检测之类的技术来允许我们在支持它们的浏览器上瞄准特征。

先看看如何建立一个移动网站

既然您已经了解了构建移动优先的响应型站点的好处及其背后的方法,那么让我们来看看如何构建移动优先的站点。

首先要开始使用移动设备,你需要把一些基本的 HTML 放在一起。在这个例子中,你将输入一些可用于博客文章的 HTML,包括标题、文章和相关文章的列表,如清单 3-1 所示。

清单 3-1。我们的移动第一个例子的 HTML

<!DOCTYPE html>

<html>

<head>

<title>Mobile First</title>

<meta name="viewport" content="width=device-width">

<link rel="stylesheet" type="text/css" href="mobile-first.css">

</head>

<body>

<header>

<h1>Blog</h1>

</header>

<div class="content" role="main">

<article>

<h2>Article title</h2>

<p>02/12/2013</p>

<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit amet fermentum. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Vestibulum id ligula porta felis euismod semper.</p>

</article>

<aside>

<h2>Related Articles</h2>

<nav>

<ul>

<li><a href="#">Article item 1</a></li>

<li><a href="#">Article item 2</a></li>

<li><a href="#">Article item 3</a></li>

</ul>

</nav>

</aside>

</div>

</body>

</html>

如果你把它加载到你的浏览器中,你会得到一个简单的没有任何样式的单列页面,如图 3-1 所示。

A978-1-4302-6695-2_3_Fig1_HTML.jpg

图 3-1。

Our mobile first example site before we have applied any styles

因为您首先构建移动设备,所以您将首先集中精力构建主 CSS 来设计页面样式。您将应用以下样式:

body: Both margin and padding set to 0px, the font size set to 14px, and the line height set to 18px   header: Add background color and padding   h1: Set the color to white   aside and article: add margin of 20px to both the left and right   article: add a border to separate the article from the related articles

将这些放在一起后,它会看起来像是我为清单 3-2 编写的代码。

清单 3-2。我们的示例 mobile first 站点的基本 CSS

body{

margin: 0;

padding: 0;

font-size: 14px;

line-height: 18px;

}

header{

background: #304480;

padding: 10px 20px;

}

h1{

color: #fff;

}

article, aside{

margin: 0 20px;

}

article{

border-bottom: 1px solid #304480;

}

如果你看一看应用了这些额外样式的网站,你会发现你的网站在我们的超小型设备上运行得很好,如图 3-2 所示。

A978-1-4302-6695-2_3_Fig2_HTML.jpg

图 3-2。

The site, as it looks on browsers with a extra small viewport

然而,如果你只是简单地将浏览器拉伸到屏幕的宽度,网站看起来就不那么好了,如图 3-3 所示。

A978-1-4302-6695-2_3_Fig3_HTML.jpg

图 3-3。

Our mobile first site, shown on a larger viewport

单栏网站在移动设备上运行良好,但是在较大的视窗中,将相关文章列表显示为侧边栏并限制网站的宽度是有意义的。

当您从超小型设备视图增加站点的视口时,您会达到小型设备的宽度,例如平板电脑。在本例中,您将把 iPad 的最小宽度 768px 作为小型设备视图的基础,并将其用于媒体查询。因此,媒体查询的目标最小宽度为 768 像素,如清单 3-3 所示。

清单 3-3。我们的媒体查询针对我们的小型设备

@media screen and (min-width: 768px){

header, .content{

width: 728px;

margin: 0 auto;

padding: 10px 20px;

}

article, aside{

float: left;

margin: 0px;

}

article{

width: 80%;

border-bottom: 0px;

}

aside{

width: 20%;

}

}

因为你已经做了所有的移动来使站点在平板电脑上工作,你只需要增加页面的宽度来利用更大的视窗,代码如清单 3-4 所示。

清单 3-4。我们针对更大视窗的媒体查询

@media screen and (min-width: 1024px){

header, .content{

width: 940px;

}

}

现在,如果你在浏览器中查看这个,你会看到网站现在被设计成一个漂亮的两栏布局,相关文章显示在侧边栏中,如图 3-4 所示。

A978-1-4302-6695-2_3_Fig4_HTML.jpg

图 3-4。

Our completed site shown on a larger device viewport

在这个例子中,我们从移动优先的角度构建了一个简单的响应博客。我们最初构建了核心样式,然后随着浏览器视窗大小的增加,我们引入了新的媒体查询,以使我们的网站能够响应视窗的宽度,并利用增加的空间。

移动优先的警告

使用移动优先的方法建立网站有一些警告,最大的警告是浏览器对媒体查询的支持程度。总的来说,浏览器对媒体查询的支持是好的,然而,最早支持媒体查询的 Internet Explorer 版本是 Internet Explorer 9。这意味着通过移动优先的方法,Internet Explorer 8 和更早版本将接收您的移动站点。您可能很乐意保留它,或者您可以选择添加一个名为Respond.js的 polyfill,这将为 Internet Explorer 8 提供有限的媒体查询支持。

在手机的限制下建立一个网站也是一个挑战;如果你像我一样,在职业生涯中使用鼠标事件(如悬停和点击)来积累经验,你会发现它们在较小的触摸屏设备上消失了,取而代之的是手指点击和滑动。

从移动优先的角度出发,我们应该从一开始就考虑用户使用的输入类型,旨在支持用户与网站交互的各种方式。我们在正确支持多种输入类型时可能会发现的一个问题是,对于某些动作,一些浏览器试图预测我们希望我们的网站如何工作。

一个例子是鼠标悬停交互;在一些触摸屏设备上,浏览器已经实现了悬停事件,以便可以切换它,当你第一次点击元素时,悬停状态会打开,第二次点击时会关闭。这样做的目的是允许用户访问原本只能在悬停状态下访问的内容(并且您不能在移动设备上悬停)。这本身增加了额外的挑战,因为具有悬停状态的区域也可能是一个链接,这意味着当用户点击它时,它应该已经有一个动作。不幸的是,媒体查询不能确定设备是否是触摸设备,并且特征检测技术也不能准确地确定用户是否正在使用触摸设备。因此,在构建网站时,我们需要考虑处理这些交互的新方法。

瞄准高像素密度显示器

移动设备具有高像素密度变得越来越普遍。对于最终用户来说,高像素密度显示器的优势显而易见,可以提供更清晰的文本和图像。受益于这场显示技术革命的不仅仅是移动设备;笔记本电脑制造商也在他们的高端电脑上提供更高质量的显示器。

考虑到这一点,在构建网站时,您需要考虑如何利用显示技术的这一变化,谢天谢地,浏览器没有让我们束手无策,因为媒体查询支持使用分辨率媒体功能测试显示器的像素密度。

当使用媒体查询来定位具有高像素密度显示的设备时,您可以使用媒体查询分辨率功能来检查最低屏幕分辨率。此功能支持三种不同类型的设备:

dpi: dots per CSS inch   dpcm: dots per CSS centimeter   dppx: dots per pixel unit, 1dppx = 96dpi

尽管这些都是受支持的单元,但浏览器供应商建议开发人员使用 dppx。如果你正在使用dpi :

Consider using "dppx" instead of "dpi", because "dpi" in CSS refers to dots per CSS inch, not dots per physical inch, so it does not correspond to the actual "dpi" of the screen. In the media query expression: print, not all, (-WebKit-min-device-pixel-ratio: 1.25), (min-resolution: 120 dpi) https://github.com/h5bp/html5-boilerplate/issues/1474

这个警告是在使用一个流行的开源模板 HTML5 boilerplate 时显示的。正如警告所指出的,dpi 是每 CSS 英寸的点数,这可能会给开发人员造成混淆,他们不知道媒体查询中的 dpi 不是屏幕的实际 dpi。

在我展示一些如何实现针对高像素密度显示器的媒体查询的例子之前,您需要了解浏览器支持。如前所述,CSS3 仍然是一个草案标准,这意味着每种浏览器都有自己的厂商前缀实现。此外,媒体查询分辨率的一些早期实现使用设备的物理分辨率,而不是其 CSS 分辨率。此外,尽管您应该使用 dppx 作为解析查询的单位,但它是在 dpi 之后添加到规范中的,因此为了与没有实现dppx的浏览器兼容,您还应该包括dpi。这意味着您在编写媒体查询时需要考虑所有这些因素,这增加了读取媒体查询时的复杂性。为了覆盖尽可能多的浏览器,可以使用以下媒体查询:

@media only screen and (-webkit-min-device-pixel-ratio: 2),

only screen and (-o-min-device-pixel-ratio: 2/1),

only screen and (min--moz-device-pixel-ratio: 2),

only screen and (min-device-pixel-ratio: 2),

only screen and (min-resolution: 192dpi),

only screen and (min-resolution: 2dppx) {

/* High resolution styles here */

}

为了正确地了解我们如何处理这些高像素密度设备,让我们看一个简单的例子,看看一个徽标在没有任何媒体查询的情况下会是什么样子,然后编写媒体查询以在其位置显示更高分辨率的图像。

首先,让我们编写一些简单的 HTML,使用图像替换技术在页面上的 H1 中添加一个徽标。我们的 HTML 如清单 3-5 所示。

清单 3-5。高分辨率图像示例的 HTML

<!DOCTYPE html>

<html>

<head>

<title>High Res Image Example</title>

<meta name="viewport" content="width=device-width">

<link rel="stylesheet" type="text/css" href="high-res-images.css">

</head>

<body>

<h1>logo</h1>

</body>

</html>

然后你需要创建 CSS。现在,您只需创建 CSS 来显示标准分辨率的徽标。让我们在徽标周围添加一个边框,以清楚地显示图像的大小。

清单 3-6。我们的标志的基本 CSS,使用正常的低分辨率图像

h1{

width: 100px;

height: 100px;

text-indent: 100%;

white-space: nowrap;

overflow: hidden;

background-image: url("low-res-image.png");

border: 3px solid #d3000c;

margin: 0px;

}

现在,当你在具有高像素密度显示器的设备上查看时,你可以看到图像的放大导致了一些模糊,如图 3-5 所示。

A978-1-4302-6695-2_3_Fig5_HTML.jpg

图 3-5。

Our lower resolution logo shown on iOS

我们现在可以看看如何添加媒体查询来处理加载更高分辨率版本的徽标。

要加载图像的更高质量版本,请使用您之前使用的查询,然后添加一些 CSS 来将背景图像更改为图像的更高分辨率版本。您还需要将背景图像缩放到原始图像的大小,以便它适合相同的尺寸(清单 3-7)。

清单 3-7。CSS 媒体查询以在高像素密度显示器上显示更高分辨率的徽标

@media only screen and (-webkit-min-device-pixel-ratio: 2),

only screen and (-o-min-device-pixel-ratio: 2/1),

only screen and (min--moz-device-pixel-ratio: 2),

only screen and (min-device-pixel-ratio: 2),

only screen and (min-resolution: 192dpi),

only screen and (min-resolution: 2dppx) {

h1{

background-image: url("high-res-image.png");

background-size: 100px 100px;

}

}

现在,如果您在高像素密度设备上进行测试,您将会看到徽标图像显示得更加清晰,如图 3-6 所示。

A978-1-4302-6695-2_3_Fig6_HTML.jpg

图 3-6。

Our high resolution logo shown on iOS

这只是一个简单的例子,说明如何使用媒体查询来改善网站在高像素密度设备上的显示。

摘要

有各种各样的设备可供使用,建立一个能够支持所有这些设备的网站是非常具有挑战性的。开发人员需要支持各种不同类型的设备,从电视到音频合成器,正如本章所讨论的,自 CSS2.1 规范以来,我们现在有能力使用媒体类型来支持这些设备。媒体类型限制了开发人员直接针对这些不同的类型,然而,现在各种各样的现代设备都使用屏幕媒体类型,这意味着尽管对开发人员来说它们也应该适合这些其他类别(如电视或手持设备)是合乎逻辑的,但事实并非如此。

这就是媒体提问的地方。与媒体类型相比,它们允许开发人员更好地控制目标样式。媒体查询允许开发人员针对设备的各种功能,包括屏幕宽度、屏幕高度和分辨率。这种细粒度的控制意味着我们可以更好地针对各种可用的设备。

媒体查询构成了响应式 web 设计的基础,因此开发人员很好地理解如何使用它们来优化网站以在各种设备上工作是非常重要的。展望未来,媒体查询将使我们能够支持越来越多的设备,随着新设备类别的不断出现,通过使用媒体查询,我们已经能够很好地支持它们。

在我们的下一章中,你将学习网页设计中通常使用的不同类型的布局,重点关注流体设计的工作原理,以及我们如何将流体设计作为一个响应式网站的一部分来实现。

Footnotes 1

http://www.phpied.com/5-years-later-print-css-still-sucks/

2

W3C 媒体类型规范, http://www.w3.org/TR/CSS2/media.html

3

steven champeon,Webmonkey .我是 steven champeon,webmonkey . http://www.hesketh.com/thought-leadership/our-publications/progressive-enhancement-and-future-web-design

4

卢克·乌鲁布莱夫斯基,http://www.lukew.com/ff/entry.asp?933=2009 年 11 月 3 日。

四、使用流体布局

已经研究了媒体查询的使用,本章将研究如何将这些与 CSS3 的其他方面结合起来,为您的用户构建更好的体验,这种体验既灵活又能够在更广泛的设备上工作。

本章将探讨:

The different types of layouts.   Principles when working with a fluid design.   Building a fluid design using a CSS grid.

布局类型

谈到页面的结构布局,有多种类型的布局可供选择。三种最流行的布局类型是固定宽度布局、流动布局和弹性布局,每种布局都有自己的优点和缺点。

当决定使用哪种类型的布局时,考虑用户的体验是很重要的。对于一个响应式站点,你的布局选择需要在大量设备上都能很好地工作。由于使用的用户数量庞大,因此选择一种能够为大多数用户提供最佳体验的布局类型非常重要。你还需要考虑到,从最小的智能手机到客厅里的 85 英寸电视,任何东西都有可能访问你的网站。

让我们来看看不同的布局风格,并考虑每种风格的优点和缺点,目的是学习如何从每种风格中选择最佳的部分,以便为网站用户建立良好的体验。

固定宽度布局

顾名思义,固定宽度布局主要是用具有固定宽度的包装器构建的。然后将其定位在屏幕上,通常位于浏览器视口的中心。无论用于访问网站的屏幕大小如何,固定宽度布局将保持其固定宽度。

从历史上看,当开发人员构建网站时,他们是从一个有固定宽度的设计开始构建的。通常,设计者会将站点设计成960px宽,因为这是使用网格布局的理想宽度,因为这个数字可以被 3、4、5、6、8、10、12 和 15 整除。当开发人员开始构建这些设计时,他们会在960px宽的地方构建站点,努力使构建尽可能像素完美。这里的想法是,他们可以让设计在浏览器中看起来像平面设计一样精彩。图 4-1 显示了一个典型的三列固定布局的例子,有两个侧栏和一个主要内容区。

A978-1-4302-6695-2_4_Fig1_HTML.jpg

图 4-1。

An example of a 960px wide, three-column fixed layout

如果你看看一个典型的固定宽度布局是如何组成的,你通常会有一个固定宽度为960px的包装器,在这个包装器里面你会有你的网站内容,通常有以像素定义的宽度。在图 4-1 的例子中,有一个宽度为576px的主要内容区域,两侧有两个192px列。

固定宽度构建仍然非常受欢迎,当您考虑到创建内容(包括完全符合设计的图像)的能力时,就很容易理解为什么了。图 4-2 展示了三星固定宽度网站的当前示例。

A978-1-4302-6695-2_4_Fig2_HTML.jpg

图 4-2。

The Samsung web site is a good example of a fixed layout

三星网站被构建为960px宽,使该网站能够针对各种流行的视窗。尽管没有响应,该网站仍然可以在移动设备上使用;它最初加载全屏,整个网站可见,然后,为了与网站交互,用户能够利用浏览器内置的捏、拉和双击用户交互来缩放和导航内容。虽然这是可用的,但它并没有提供我们在移动设备上期望的奇妙的用户体验。

如果你在 iPhone 上的 Mobile Safari 中查看三星网站,你会看到如图 4-3 所示的图像。

A978-1-4302-6695-2_4_Fig3_HTML.jpg

图 4-3。

Samsung desktop site (left) vs. Samsung mobile site (right)

三星确实提供了特定的移动网站,如图 4-3 所示,然而,它并不与桌面网站共享相同的内容。这意味着用户最终仍然可以在他们的移动设备上使用桌面站点来访问通过其他方式无法访问的内容。

固定宽度设计的一个主要问题是,你在判断你的站点应该设计和建造到什么宽度。做出这种决定的局限性在于要支持的浏览器宽度范围很大。这样做的结果是,在小于所选固定宽度的浏览器窗口上,你会得到水平滚动,而在较大的浏览器窗口上,你的站点两侧有多余的空间可以得到更好的利用。

当苹果在 2007 年推出 iPhone 时,大多数网站都是固定宽度的,因此开发者试图通过默认缩小网站来弥补这一点(按照三星的说法),这意味着用户可以看到网站的整个宽度,尽管对他们的设备来说太大了。虽然这比仅仅看到网站的一角提供了更好的体验,但浏览一个大型网站仍然会令人沮丧。

使用固定宽度的设计不一定是一个坏的决定,重要的是要考虑你的网站的用途和目标受众。固定宽度的场地适用于希望在所有视口中保持相同布局和比例的场地。

弹性布局

弹性布局不以像素定义宽度,而是以 ems 度量。em 单位是字体大小的倍数,所以如果你设置你的字体大小为16px,那么2em的宽度将等于32px。这意味着,如果用户在查看网站时更改字体大小,网站的布局也将随着字体大小的增加或减少而成比例地改变。

弹性布局给了开发人员更多的控制,因为当用户在浏览器中调整文本大小时,设计比例保持不变。这意味着,如果用户需要增加你的网站上使用的字体大小,他们在你的网站上获得的体验与用户以原始字体大小查看网站没有什么不同。站点元素的大小与字体大小成比例。这意味着弹性布局在使开发者确保他们的站点对所有用户都是可访问的方面非常有效。

如果您采用原始的固定宽度网站设计,使用 16px 的基本字体大小,您可以通过将每个像素列宽除以字体大小(在本例中为 16)来轻松地将布局转换为使用 em 而不是像素。图 4-4 显示了图 4-1 中使用的相同布局,用于转换为弹性布局的固定布局示例。

A978-1-4302-6695-2_4_Fig4_HTML.jpg

图 4-4。

An example of a 960px wide, three-column elastic layout with em conversion

该示例现在使用 ems 而不是像素来定义列宽。从像素到 ems 的计算非常容易执行:

像素宽度/基本字体大小= em 宽度

使用这个简单的公式,您可以很容易地计算出 ems 中元素的宽度。在图 4-4 中,您可以看到如何通过将像素宽度代入计算中来计算元素宽度:

576 / 16 = 36em

192 / 16 = 12em

重要的是要知道,ems 可以有一个小数值,所以如果您的计算没有得到一个整数,这不是一个问题。

使用弹性布局的一个例子是北爱尔兰社区档案馆关于阿尔斯特种植园的微型网站,如图 4-5 所示。 1

A978-1-4302-6695-2_4_Fig5_HTML.jpg

图 4-5。

The Plantation site is a good example of an elastic layout

这个网站使用 ems 来定义外部包装的宽度和布局的列。如果增加了基本字体大小,整个网站就会调整大小以适应更大的文本,如图 4-6 所示。

A978-1-4302-6695-2_4_Fig6_HTML.jpg

图 4-6。

The Plantation site after increasing the base font size

弹性布局最初的问题之一是,因为站点的宽度是基于基本字体大小的,如果用户将字体大小增加到某一点以上,站点在视口中会变得非常大,可能会导致浏览器出现水平滚动条。

弹性布局的另一个问题是,它们本质上仍然是一种固定宽度的布局,我的意思是弹性是基于基本字体大小的,所以除非用户改变字体大小,否则网站将保持开发者想要的基本宽度。这意味着,虽然弹性布局提供了比基于像素的固定布局更易访问的方法,但是布局仍然看起来在每个断点处都咬合到位,因为应用了每个媒体查询内的 CSS 因此,它们之间没有流动性,因为网站适应不同的大小。

弹性布局不太流行的原因之一是因为在 ems 中必须计算宽度所带来的困难。在查看基本字体大小之前,不清楚36em的宽度是多少像素。基本字体大小为16px,宽度为576px;然而,通过简单地改变基本字体大小为14px,宽度将减少到504px。不得不执行这个(虽然简单)计算给开发人员增加了一个额外的步骤。

你可能面临的另一个问题是ems是相对于父节点计算的;因此,如果父元素定义了不同于正文的字体大小,您可能会发现元素的宽度不是您所期望的。这是因为您可能已经根据正文的字体大小计算了宽度;要解决这个问题,只需根据父元素的字体大小重新计算即可。为了完全避免这个问题,一个选择是对字体大小使用 rem 而不是 em,这意味着所有的字体大小都相对于基本字体大小,而不管父元素的字体大小,警告是 rem 在诸如 Internet Explorer 8 和更早版本的传统浏览器中不工作。

流畅的布局

流体布局也称为液体布局或相对布局,其宽度根据用户的视口而变化。与固定布局不同,在固定布局中,宽度以像素为单位定义,相反,您可以将宽度定义为百分比,其中百分比引用其在视口中的部分,如图 4-7 所示。

A978-1-4302-6695-2_4_Fig7_HTML.jpg

图 4-7。

An example of a fluid web site, with browser currently open at 960px wide

如果你以固定宽度布局的图 4-1 中的例子为例,将尺寸转换成百分比,你将有两列宽度为 20 %,一列宽度为 60%。浏览器视口在960px时,等效像素值与固定示例中的像素值相同(括号中显示的像素),如图 4-8 所示。

A978-1-4302-6695-2_4_Fig8_HTML.jpg

图 4-8。

An example of a fluid web site, with the browser open at 1,600px wide

如果您随后要调整浏览器的大小,使视窗宽度为1,600px,网站会自然地放大到浏览器的全宽。较小的列现在的宽度为320px,较大的列的宽度为960px。这样做的好处是内容会自然地填充页面,潜在地放大任何图像并使文本流动以填充可用空间。

雅虎!最近重新设计了 Flickr 网站,使用了一种流体布局方法(如图 4-9 所示),利用额外的可用空间来增加图像的大小,并根据屏幕大小,增加每行的图像数量。这样做的好处是网站的用户可以更快地浏览更多的图片,并且更加关注网站。

A978-1-4302-6695-2_4_Fig9_HTML.jpg

图 4-9。

The Flickr web site is a good example of a fluid web site

如果您要调整浏览器窗口的大小,Flickr 站点会随着浏览器一起缩放,从而更好地利用可用空间。图像要么增加尺寸,要么允许在一行中显示更多,利用用户的观看空间,并最大限度地增加他们在任何给定时间能够观看的内容。你可以在图 4-10 中看到 Flickr 网站的全景图。

A978-1-4302-6695-2_4_Fig10_HTML.jpg

图 4-10。

The Flickr web site when viewed on a wider screen

使用流体布局对 Flickr 的好处是显而易见的;图像可以填满所有可用的空间,允许用户适当地欣赏照片。用户体验的改善也可以通过网站参与度的增加来衡量。虽然雅虎!没有公布任何确切的数字,一个名叫托马斯·霍克斯 2 的 Flickr 用户自己做了一些研究。在重新设计启动后的六天内,有 8000 万张新照片上传到网站,而在重新设计前的六天,用户仅上传了 4700 万张照片。

流畅的布局不仅有利于像 Flickr 这样只有图片的网站,也有利于平铺内容的网站。Pinterest 就是一个例子,虽然它也非常注重图片,但也显示其他内容。对于 Pinterest,用户经常会浏览他们感兴趣的版块,寻找吸引他们注意力的东西,通过使用流畅的布局,Pinterest 能够显示用户当时能够查看的最大数量的内容,让他们更快地找到他们想要的东西。Pinterest 的流体宽度、平铺界面如图 4-11 所示。

A978-1-4302-6695-2_4_Fig11_HTML.jpg

图 4-11。

The tiled interface of Pinterest benefits from fluid design to show more content

使用流体布局的一个好处是,它可以更加用户友好,因为它可以根据用户的设备进行调整。流体布局可以利用可用空间,让内容填满所有可用空间,而不是有多余的空白空间,在较大的设备上,它允许内容间隔更大,有额外的呼吸空间。

尽管流动布局非常强大,但它们也有一些缺点,其中最重要的是它们会导致内容呈现方式的问题。由于流畅的布局可以在任何浏览器宽度下查看,您可能会发现图像的外观存在问题。虽然可以随着浏览器的大小调整来缩放图片,但重要的是要知道有些图片的缩放效果不好。包含焦点的图像会受到影响,因为缩小它们不仅会失去效果,还会使焦点太小。同样,低分辨率的图片在放大时,质量会显著下降。

除了内容呈现方面的问题之外,fluid width 网站可能还会面临一些交互功能方面的问题。网站通常使用 JavaScript 来增强用户体验,例如,显示等高的列。当用户调整浏览器大小时,问题就来了;文本重排,然后列变得太高或太短。因此,JavaScript 需要在调整大小时重新计算高度。

为什么要在响应式设计中使用流体布局

探讨了固定布局、流动布局和弹性布局之后,很重要的一点是要很好地理解每种方法的优缺点。然而,我还没有真正讨论这一切与响应式设计的关系。

在第三章中,我解释了如何使用媒体查询使你的网站适应不同的视窗。这样做的问题是,无论是固定布局还是弹性布局,它们都会在适应不同屏幕尺寸的两种或更多种不同布局之间切换。它们不是对各种不同的设备做出反应,而是适应于在几种不同的普通屏幕尺寸上工作。

但是设备不再符合几种不同的普通屏幕尺寸;相反,开发人员现在面对的浏览器具有任何尺寸的视窗,可以在从 85 英寸电视到 3.5 英寸手机屏幕的设备上观看。开发人员必须有这样的心态,无论如何都希望他们的网站看起来很棒,而不仅仅是在几个选择的屏幕尺寸上。如果用户要调整浏览器的大小,你会希望调整平滑流畅,布局在任何给定的分辨率下都能工作。

当考虑选择布局样式时,您应该确保您熟悉可用的替代选项,这就是为什么我在前面的部分中介绍了优点和缺点。在构建网站时,你应该对你选择的布局风格感到满意,并根据你的需要进行调整,而不是受它们的限制。也就是说,响应式设计确实需要一个流畅的布局,以便在最大范围的设备上提供最佳的用户体验,同时在不同的视窗大小之间保持最小的中断。这并不意味着你不能有断点,这导致布局捕捉改变它,以更好地利用可用的屏幕空间。事实上,这是受到鼓励的;你会希望能够以最好的方式利用你现有的空间。

处理流体设计时的原则

构建流体设计可以为响应式设计带来多种益处;然而,有一些重要的原则要记住,以确保您的网站保持可用,无论视窗的大小。

您应该努力遵循的主要原则是:

Do not use fixed heights.   Do not necessitate horizontal scroll bars.   Think about how your images look at different sizes.   Think about wrapping content.   Think about spacing.   Think about the length of your lines of text.

让我们更详细地探讨这些问题,看看解决您可能遇到的问题的潜在方法。

不要使用固定高度

如果您习惯于构建固定的布局,您很可能会遇到这样的问题:您已经为元素定义了固定的高度,但是当内容发生变化时,该高度或者有多余的间距,或者内容溢出到定义的高度之外。

随着流体设计带来的可变宽度,这个问题变得更加普遍,因为内容换行的方式会根据视口的大小而改变,所以在 CSS 中设置固定的高度变得不切实际。

不幸的是,有时在相同的栏中显示内容是设计的一部分,每个栏都有自己的背景颜色。如果不在 CSS 中定义高度,这可能很难实现;因此,要么在 CSS 中创建假列,要么使用 JavaScript 动态设置列的高度。

使用 CSS 创建假列

简单地使用一些 CSS 就可以创建列的效果。

对于两列布局,您知道一列总是比另一列高,只需将父元素的背景色设置为较短列的背景色。然后,通过设置 longer column 元素的背景色,您已经创建了具有两列的效果。让我们快速看一下处理这个问题的代码,从一些简单的 HTML 开始:

<div class="col-container">

<aside class="col">

Sidebar

</aside>

<div class="col main">

Main Content Area

</div>

</div>

如上所述,您将把列的背景应用到列容器和主内容列。为了实现列的效果,您还可以将列的宽度定义为 50 %,向左浮动,使它们彼此相邻:

.col-container{

background: #000;

color: #fff;

}

.col-container:after{

content: ' ';

clear: both;

display: block;

}

.col{

float: left;

width: 50%;

}

.col.main{

background: #999;

}

值得注意的是,您已经向列容器添加了一个伪元素来清除浮动的列。这是必要的,以便浏览器计算列容器本身的高度。如果您的站点已经为此使用了 clearfix 类,那么您可以选择将它添加到您的列容器中,而不是添加一个特定于该元素的伪元素。

如果你在你的浏览器中签出这个,布局有两列和预期的一样高,如图 4-12 所示。

A978-1-4302-6695-2_4_Fig12_HTML.jpg

图 4-12。

Two columns with equal height using CSS

虽然这很简单,但是这种两列的方法适用于所有的浏览器,并且非常有效,需要注意的是这种方法只适用于两列,并且需要知道哪一列更高。

如果有两列以上,可以选择在父级上使用 CSS3 渐变来达到相同的效果。但是,请记住,这种方法只能在 Internet Explorer 10 或更高版本中使用。扩展上面的示例,您可以在 HTML 中添加一个额外的列:

<div class="col-container">

<aside class="col">

Sidebar

</aside>

<div class="col main">

Main content area with our main body content

</div>

<div class="col">

Related content

</aside>

</div>

然后,您可以简单地修改现有的 CSS,因为您不再需要向主列添加背景。因此,您可以删除 CSS 的这一部分。然后将 CSS3 背景渐变添加到列容器中。在下面的示例中,您只包括 W3C CSS3 属性,对于实时代码来说,添加带前缀的版本以确保浏览器支持是很重要的:

.col-container{

background: linear-gradient(to right, #000000 0%,#000000 33%,#a0a0a0 33%,#a0a0a0 66%,#a0a0a0 66%,#707070 66%);

color: #fff;

}

.col-container:after{

content: ' ';

clear: both;

display: block;

}

.col{

float: left;

width: 33.3%;

}

当在浏览器中查看运行结果时,新列以正确的背景颜色出现,如图 4-13 所示。

A978-1-4302-6695-2_4_Fig13_HTML.jpg

图 4-13。

Three columns with equal height using CSS

如果您需要支持旧版本的 Internet Explorer,或者您想要支持动态数量的列,您可能应该考虑使用 JavaScript。

使用 JavaScript

已经了解了如何使用 CSS 创建仿列效果,让我们看看如何使用 JavaScript 创建等高的列。JavaScript 方法并不试图伪造列的外观,而是让所有的列都具有相同的高度。

使用 JavaScript 创建等高列的方法是遍历每个列,找到最高的列,然后将所有列设置为最高列的高度。

为了演示这一点,让我们看一个简单的例子,从一些 HTML 开始:

<div class="col-container">

<aside class="col nav">

Sidebar

</aside>

<div class="col main">

Main content area with our main body content

</div>

<div class="col related">

Related content

</aside>

</div>

然后,您需要设置列的样式,使它们具有不同的背景颜色:

.col-container{

color: #fff;

}

.col-container:after{

content: ' ';

clear: both;

display: block;

}

.col{

float: left;

width: 33.3%;

}

.col.nav{

background: #aaa;

}

.col.main{

background: #000;

}

.col.related{

background: #999;

}

有了这个地方,现在所有的列都很好,每个都有独特的背景颜色;然而,柱子的高度仍然不相等。你可以在图 4-14 中看到它的样子。

A978-1-4302-6695-2_4_Fig14_HTML.jpg

图 4-14。

Three columns with unequal height before adding the JavaScript

为了使列相等,您需要添加一些 JavaScript。在下面的例子中,我使用了document.getElementsByClassName,它只受 Internet Explorer 9 或更高版本的支持。但是,您可以使用聚合填充来支持此方法,此要点code : https://gist.github.com/eikes/2299607 中提供了一个这样的聚合填充。

<script>

var columns = document.getElementsByClassName('col'),

height = 0;

//Loop through columns and find the tallest columns

for (var i = 0; i < columns.length; i++) {

if(height < columns[i].clientHeight){

height = columns[i].clientHeight;

}

}

//Apply the max height to all columns

for (var i = 0; i < columns.length; i++) {

columns[i].style.height = height + "px";

}

</script>

equal column JavaScript 简单地遍历所有列,找到最高的元素。然后,它再次遍历元素来定义高度。你可以看到 JavaScript 在图 4-15 中工作。

A978-1-4302-6695-2_4_Fig15_HTML.jpg

图 4-15。

Three columns with equal height after adding the JavaScript

对于固定的、非响应性的设计,在页面加载后简单地运行这个 JavaScript 就足够了。然而,对于一个流畅的、响应性的设计,用户可以调整浏览器的大小,因此 JavaScript 需要能够在调整大小时更新列的高度:

<script>

var equalColumns = function(){

var columns = document.getElementsByClassName('col'), height = 0;

//Reset the height of all the columns so that we can recalculate max height

for (var i = 0; i < columns.length; i++) {

columns[i].style.height = "auto";

}

//Loop through columns and find max height

for (var i = 0; i < columns.length; i++) {

if(height < columns[i].clientHeight){

height = columns[i].clientHeight;

}

}

//Apply the max height to all columns

for (var i = 0; i < columns.length; i++) {

columns[i].style.height = height + "px";

}

}

//Initially set the column heights

equalColumns();

//Update column heights on browser resize

window.addEventListener("resize", equalColumns, true);

</script>

有了这个修改后的 JavaScript,您已经将我们的equalColumns JavaScript 包装成一个函数,并对其进行了更新,以便它在重新计算高度之前重置所有列的高度。然后,当页面加载并将其作为事件侦听器附加到 resize 事件时,运行该方法。现在,如果用户调整浏览器的大小,列的高度也会随之调整。

不需要水平滚动条

当使用一个网站时,内容通常会沿着页面向下流动,因此能够双向滚动会损害用户的体验。

有一个创建水平布局的用例;然而,在这种情况下,你应该避免使用垂直滚动条,专注于提供水平体验。你可能想要建立一个水平布局的例子是一个艺术家或摄影师的在线作品集,比如 C. L. Holloway 的作品集网站,他通过允许用户水平滚动创建了一个类似画廊的体验。 3 需要注意的是,尽管这个网站的开发者选择了使用水平滚动条,但该网站选择了避免垂直滚动条,除非视口太浅而无法显示所有内容(大约在610px,这非常适合最流行的垂直屏幕分辨率)。

想想你的图片在不同尺寸下看起来是什么样的

当使用流体设计在网站上实现图像时,仔细观察图像在不同尺寸下的外观是很重要的。你需要考虑如何缩放你的图片,这取决于你是想把图片作为标签放在你的页面上,还是想把图片作为背景图片。

缩放内嵌图像

要缩放内嵌图像,只需对图像应用宽度。如果您的站点是响应式的,您实际上并不知道将哪个宽度大小应用于图像,因此您可以将max-width属性设置为 100 %,而不是定义绝对宽度,如以下代码所示:

img {

max-width:100%

}

有了这个,如果一个图像比它的容器大,它将缩小;但是,如果图像比容器小,它将保持原来的大小。

缩放背景图像

如果要缩放背景图像,可以使用 CSS3 background-size属性。这可以采用三个不同的值:覆盖、包含和宽度/高度值。

.image{

background-size:80px 60px;

}

background-size的问题在于,虽然你可以使用相对宽度来设置元素的宽度,但是对于元素的高度来说,这并不容易。

第一步是定义将包含背景图像的div:

<div class="image"></div>

接下来你需要定义 CSS。首先,您将定义图像容器。因为您希望宽度是相对的,所以您将使用一个百分比值,在本例中为width: 100%。图像的高度需要与图像的宽度成比例;使用 height 属性无法实现这一点,因为高度不能是相对的。相反,你可以使用填充。因为百分比填充是基于元素的宽度,所以您可以使用它来定义高度。不幸的是,这是有点复杂的地方,因为你需要设置填充底部,以便它保持图像的正确纵横比。

有了正确的宽度和高度,您现在只需添加图像作为背景图像,并设置background-size使其缩放到元素的全宽和全高:

.image{

width: 100%;

position: relative;

padding-bottom: 125%;

background: url(scalableimage.jpg) 0 0 no-repeat;

background-size: 100% 100%;

}

有了这个,你可以在浏览器中查看图像,看到它现在与页面的宽度成比例。图 4-16 显示了它在浏览器中的样子。

A978-1-4302-6695-2_4_Fig16_HTML.jpg

图 4-16。

Scaling a background image with CSS

考虑包装内容

当构建一个流动的站点时,元素不断变小并不总是有意义的,因为最终内容会变得不稳定和不可读。相反,随着视口变大,将内容块堆叠在一起是没有意义的。这两种情况下的选项都是考虑内容如何在不同的视口换行。

这种情况的一个例子是,在网站的一侧有一个包含相关内容的侧边栏。如果侧边栏占页面宽度的 25 %,而视窗只有 320 像素宽,那么该列只有 80 像素宽。侧边栏可以移动到页面主要内容的下方,为主要内容释放额外的空间,并允许侧边栏也变成全宽,而不是保持栏的结构。

想想间距

使用流体设计时,使用元素宽度的百分比,好处是浏览器能够根据视口的宽度缩放宽度。除了用百分比计算元素的宽度,您还需要考虑如何处理每个元素之间的间距。

增加间距的一种方法是使用填充的百分比值。这里的问题是,在较大的视窗上,间距会太大,而在较小的视窗上,间距会太小。

理想情况下,您希望用百分比定义元素宽度,用像素或 ems 定义元素填充。不幸的是,默认情况下,浏览器呈现盒子模型的方式将填充放在宽度的外部,这意味着当定义填充的像素时,设计的流畅度会随着水平滚动条的出现而破坏,如图 4-17 所示。

A978-1-4302-6695-2_4_Fig17_HTML.jpg

图 4-17。

Using pixel values for paddings, the Fluidness of the design breaks and horizontal scrollbars appear

如果你从事 web 开发已经有一段时间了,你可能知道 quirks 模式。从本质上讲,在旧的浏览器中,这些标准没有得到正确的实现,而到了 Internet Explorer 6,人们决定要实现 HTML 和 CSS 标准,就需要为没有更新以支持这些标准的旧站点留一条退路。这种后退被称为 quirks 模式,它是通过在 HTML 文档的第一行不包含doctype来触发的。

特别是,在这些旧的浏览器中,与标准相比,盒子模型的实现是不正确的。W3C 标准要求,当定义元素的宽度或高度时,它不包括填充、边框和边距,这些应用于宽度或高度的外部。在这些较旧的浏览器中,实现包括内容、填充和边框,都在指定的宽度或高度内。

quirks 模式的实现对于允许你增加你的流体设计站点的间距是理想的,但是,你不想强迫你的站点进入 quirks 模式,因为你希望你的站点是标准兼容的。随着 CSS3 的到来,对盒子模型如何工作的控制权交给了开发人员,我们现在能够使用新的盒子大小 CSS 属性来确定盒子如何呈现。

box-sizing 属性提供了三个值供我们选择,每个值将呈现一个具有不同版本的盒子模型的盒子。可用于调整框大小的值有内容框、填充框和边框框。

内容盒

默认样式由 CSS 标准指定。宽度和高度属性被测量为仅包括内容区域。边框、边距和填充被添加到外部。示例如图 4-18 所示。

A978-1-4302-6695-2_4_Fig18_HTML.jpg

图 4-18。

The box model as applied by setting the CSS property box-sizing to content-box

填充盒

对于 padding-box,width 和 height 属性包括填充大小,但不包括边框或边距。示例如图 4-19 所示。

A978-1-4302-6695-2_4_Fig19_HTML.jpg

图 4-19。

The box mode asl applied by setting the CSS property box-sizing to padding-box

边框

width 和 height 属性都包括填充和边框,但不包括边距。如果您已经开发了一段时间,您可能会熟悉 border-box 的工作方式,因为它是 Internet Explorer 在文档处于 quirks 模式时使用的。示例如图 4-20 所示。

A978-1-4302-6695-2_4_Fig20_HTML.jpg

图 4-20。

The box model as applied by setting the CSS property box-sizing to border-box

边框是响应式开发的圣杯,因为它允许我们向元素添加一致的填充,同时仍然使用基于百分比的宽度。

对于一个响应式站点来说,对所有元素使用边框是完全有意义的。它不仅保持了一致性,还意味着您可以对从div到输入字段的所有内容使用百分比宽度。将边框应用于所有元素的方法是使用通用选择器(*):

* {

-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */

-moz-box-sizing: border-box;    /* Firefox, other Gecko */

box-sizing: border-box;         /* Opera/IE 8+ */

}

想想你的文本行的长度

当构建一个流畅的站点时,不管浏览器大小如何,简单地允许一个站点是全幅的是非常容易的。然而,这通常会导致很长的文本行。

Chris Coyier 在 2013 年 11 月就这个问题写了一篇关于 CSS 窍门的文章:

The traditional idea is that the text (long text, multiple paragraphs, need to look at it again when reading ...) should be between 45 and 75 characters per line. It's awkward when it's short, and it's easy to find the position and the next line when it's long.

这完全有道理;一眼看去,不算过长的台词读起来更舒服;报纸使用专栏也是出于同样的原因。在流畅的布局中,很容易让文本行变得过长。为了避免这种情况,您可以使用一个名为max-width的 CSS 属性,它是作为 CSS2.1 规范的一部分引入的。

CSS max-width属性允许你定义一个元素可以跨越的最大宽度,使你能够通过直接定义包含元素或者段落标签的最大宽度来控制文本行的长度。在定义这个最大宽度时,实际上可以通过在 ems 中定义您的max-width来考虑每行的最大字符数。这意味着元素的最大宽度将保持相对于定义的字体大小,因此如果用户增加默认字体大小,元素将适当缩放。需要注意的是,使用像素宽度来包含 em 定义的宽度是很棘手的,所以与您定义宽度的方式保持一致是很重要的。

同样,您也希望确保您的文本行不会显得太短。为此,您可以使用 CSS 属性min-width,它也是作为 CSS2.1 规范的一部分引入的。min-width属性允许你指定一个元素可以拥有的最小宽度,如果元素的宽度低于min-width值,它会覆盖widthmax-width属性的值。对于单行文本来说,副本太短的情况,min-width属性没有帮助;但是,它确实有助于防止出现这样的情况:由于元素不够宽,多行文本每行只有几个单词。需要注意的是,如果容器的宽度小于元素的宽度,那么元素将溢出容器。此外,如果容器的 CSS 属性 overflow 设置为 hidden,元素的内容将被裁剪,这当然是不希望的。

浏览器对max-widthmin-width的支持在 Internet Explorer 7 和更高版本、Firefox、Chrome 和 Safari 中都很好,包括移动版本,它们都支持该属性。

使用 CSS 网格构建流畅的设计

至此,您已经很好地理解了构建流体设计时应遵循的原则。让我们来看看如何将这些付诸实践,以构建一个流畅的设计。在第三章中已经构建了一个响应式站点,当你学习了媒体查询时,让我们看看如何使用流体设计原理构建一个响应式 CSS 布局网格。

布局网格起源于出版业,在出版业中,公司会使用预定义的网格来布局杂志和书籍等印刷材质。这样做的目的是让他们在整个印刷作品中实现视觉对称。这些不可见网格的目的是为读者提供一个更简单、一致的阅读体验。早在响应式设计成为趋势之前,印刷品中的网格所带来的好处就促使它们以 CSS 网格的形式向网络过渡。

CSS 网格通常使用列来构建;这些是网格的最小度量单位,块可以跨越一列或多列。通常,一个网格有 12 到 16 列,每列之间会有间距。间距是每列之间的间距,通常定义为填充或边距。

为了让我更容易说明网格是如何工作的,我把一个固定布局网格的例子放在一起,如图 4-21 所示。在本例中,我们有一个四列网格,每列宽 215 像素,每列的两边都应用了 20 像素的装订线。

A978-1-4302-6695-2_4_Fig21_HTML.jpg

图 4-21。

An example of a fixed width grid

固定宽度的布局允许我们描绘网格是如何分布的,但是我们希望网格是流动的。为了实现这一点,我们将把我们的每一列定义为 25%的宽度,然后为我们的站点启用槽,我们将使用 box-sizing 属性来移动我们的列内部的填充。总宽为 960 像素,每列宽为 240 像素;但是,如果您要将其缩小到 320 像素,则浏览器中的列会随着每列而缩放,而不是每列 80 像素宽。该流体网格如图 4-22 所示。

A978-1-4302-6695-2_4_Fig22_HTML.jpg

图 4-22。

Grid adapted to fluid layout

构建网格

虽然您可以简单地使用 CSS 网格框架,但首先重要的是要很好地理解网格框架是如何工作的,这样您就可以充分利用它们。让我们先看看如何编写一个简单的移动响应 CSS 网格。

最简单的形式是,CSS 网格由以下内容组成:

Columns   Gutters   Rows   Wrappers

让我们更详细地看看网格的这些元素:

在开发 CSS 网格时,您需要决定网格系统将使用的列数,正如前面提到的,网格系统通常有 12 或 16 列。

CSS 网格中的列通常宽度相等,位置相邻。通常,这是通过将列向左浮动来实现的:

.col{

float: left;

width: 25%;

}

对于您将要编写的示例 CSS 网格,您将使用四列。您可能还记得我之前提到过,您可能希望您的内容跨越多列,因此为了支持这一点,您需要做一些调整。通过简单地定义每个支持的宽度,很容易使网格支持跨越多个列。之前我将一个列命名为.col,但是,为了使您能够使用一个选择器将共享样式应用到我们的附加列,您将把它重命名为".col-span-x",其中 x 是该列应该跨越的列数..列名应该反映它们将跨越多少列:

[class*="col-span"]{

float: left;

}

.col-span-1{

width: 25%;

}

.col-span-2{

width: 50%;

}

.col-span-3{

width: 75%;

}

.col-span-4{

width: 100%;

}

你可能已经注意到,在这里我使用了一种我在本书中没有涉及的 CSS 语法,一种你以前可能没有使用过的语法,叫做属性通配符选择器。使用的属性通配符选择器[class*="col-span"]允许您定位所有列,而不必添加额外的类。在这个例子中,我用它来代替书写:

.col-span-1, .col-span-2, .col-span-3, .col-span-4{

float: left;

}

到目前为止,您已经创建了四列,但是,在这里您将希望能够根据视窗的大小来调整您的站点。当我讨论构建响应式流体设计的原则时,我提到过将某些内容调整得过小会带来糟糕的用户体验。因此,您应该能够控制内容在较小设备和较大设备上分别占据多少列。您可以通过改进具有媒体查询的类来做到这一点:

[class*="col-span"]{

float: left;

}

.sm-col-span-1{

width: 25%;

}

.sm-col-span-2{

width: 50%;

}

.sm-col-span-3{

width: 75%;

}

.sm-col-span-4{

width: 100%;

}

@media screen and (min-width: 768px){

.lg-col-span-1{

width: 25%;

}

.lg-col-span-2{

width: 50%;

}

.lg-col-span-3{

width: 75%;

}

.lg-col-span-4{

width: 100%;

}

}

由于您首先构建的是可移动的 CSS 网格,因此您首先定义了默认的列宽,然后为更大的设备定义了类,如果设备的最小宽度为 768px,这些类将被激活。您已经将原来的类改为前缀为sm-lg-sm-前缀类针对小型及以上设备,lg-前缀类针对较大设备。其思想是将带有sm-前缀的列类应用于 HTML 元素,为较小的设备定义布局,然后使用带有lg-前缀的列在适当的地方覆盖列跨度。

沟壑

CSS 网格的间距是每列之间的间距;从历史上看,这是通过保证金实现的。但是,在处理流体设计时,您希望间距成为列的一部分,因此可以选择对列宽使用百分比。因此,您应该对列之间的间距使用填充。

使用空白填充的缺点是,它会阻止您为列添加背景色。这样做的原因是,如果你给列添加一种背景色,背景也会出现在槽中。解决这个问题的唯一方法是向列内的元素添加任何背景色,而不是向列本身添加背景色。

之前我解释了 CSS3 box-sizing属性,它允许你改变盒子模型的工作方式,允许你控制填充是在宽度的内部还是外部。对于此网格,您将希望列包括填充,这将用于装订线。为了实现这一点,这些列将把box-sizing设置为border-box。如果您已经像我之前建议的那样将它应用到通用选择器,您不需要在这里再次添加box-sizing:

[class*="col-span"] {

box-sizing: border-box;

padding-left: 15px;

padding-right: 15px;

}

如您所见,您还向列添加了填充。您已经使用完整的padding-leftpadding-right属性名称添加了填充,因为这可以防止您覆盖添加到元素顶部或底部的任何填充,而这些填充可能已经在 CSS 的其他地方指定了。

行用于包含列。因为列是向左浮动的,所以它们被从页面流中取出;因此,浮动元素前后的非定位块元素就像不存在一样。因此,为了防止这个问题,行还负责清除列使用的浮点数。

通常的方法是添加一个伪元素作为清除浮动的行的一部分:

.row:after{

content: ' ';

clear: both;

display: block;

}

如上所述,伪元素是一个应用了clear: both的空块级元素。

包装材质

最后,您将在所有行周围添加一个包装器。它用于向站点的左侧和右侧添加额外的填充:

.wrapper{

padding-left: 15px;

padding-right: 15px;

}

将网格放在一起

前面的例子着重于如何构建一个响应式博客,这样就有可能看到这些例子之间的差异,因此这个新代码将为一个博客构建一个响应式网格。

我已经解释了组成这个网格的 CSS,所以让我们把它们放在一起(清单 4-1)。

清单 4-1。用于网格的示例 HTML

<!DOCTYPE html>

<html>

<head>

<title>Responsive Grid</title>

<meta name="viewport" content="width=device-width">

<link rel="stylesheet" type="text/css" href="responsive-grid.css">

</head>

<body>

<header>

<div class="wrapper">

<div class="row">

<div class="sm-col-span-2 lg-col-span-4">

<h1>Blog</h1>

</div>

<nav class="sm-col-span-2 lg-col-span-4">

<ul>

<li><a href="#">Latest posts</a></li>

<li><a href="#">Popular posts</a></li>

</ul>

</nav>

</div>

</div>

</header>

<div class="content" role="main">

<div class="wrapper">

<div class="row">

<article class="sm-col-span-4 lg-col-span-3">

<h2>Article title</h2>

<p>02/12/2013</p>

<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit amet fermentum. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Vestibulum id ligula porta felis euismod semper.</p>

</article>

<aside class="sm-col-span-4 lg-col-span-1">

<h2>Related Articles</h2>

<nav>

<ul>

<li><a href="#">Article item 1</a></li>

<li><a href="#">Article item 2</a></li>

<li><a href="#">Article item 3</a></li>

</ul>

</nav>

</aside>

</div>

</div>

</div>

</body>

</html>

现在让我们把 CSS 放在一起;所以 CSS 很容易理解,让我们把 CSS 网格从站点特定的样式中分离出来,使它看起来像一个博客。完整的 CSS 代码如清单 4-2 所示。

清单 4-2。网格的完整 CSS

/*Grid styles*/

.wrapper{

padding-left: 15px;

padding-right: 15px;

}

.row:after{

content: ' ';

clear: both;

display: block;

}

[class*="col-span"] {

float: left;

box-sizing: border-box;

padding-left: 15px;

padding-right: 15px;

}

.sm-col-span-1{

width: 25%;

}

.sm-col-span-2{

width: 50%;

}

.sm-col-span-3{

width: 75%;

}

.sm-col-span-4{

width: 100%;

}

@media screen and (min-width: 768px){

.lg-col-span-1{

width: 25%;

}

.lg-col-span-2{

width: 50%;

}

.lg-col-span-3{

width: 75%;

}

.lg-col-span-4{

width: 100%;

}

}

/*Site specific*/

body{

margin: 0;

padding: 0;

font-size: 14px;

line-height: 18px;

}

header{

background: #304480;

padding-top: 10px;

padding-bottom: 10px;

color: #fff;

}

ul{

padding: 0px;

margin: 0px;

}

ul li{

list-style: none;

}

header nav{

text-align: right;

}

header nav a{

color: #fff;

}

@media screen and (min-width: 768px){

header nav{

text-align: center;

padding-top: 10px;

border-top: 1px dashed #ccc;

margin-top: 10px;

}

header nav li{

display: inline-block;

}

}

我们现在可以看看这在一个额外的小设备上看起来如何(如图 4-23 所示)。

A978-1-4302-6695-2_4_Fig23_HTML.jpg

图 4-23。

Our site shown on an extra small device (in this example an iOS device)

已经看到我们的网站在一个额外的小设备上的样子,我们还应该看看它在更大的设备上的样子,我们可以通过启动桌面浏览器并加载我们的网站来做到这一点。图 4-24 显示了我们期望看到的情况。

A978-1-4302-6695-2_4_Fig24_HTML.jpg

图 4-24。

Our site shown on a larger device (in this example a desktop browser)

但是,如果您将视窗宽度进一步增加到 1,440px,您将开始注意到文章中的文本行开始变得相当长。如前所述,每行的最佳字符数在 45 到 75 之间,查看图 4-25 中的截图,您会注意到文本远远超出了这个范围。

A978-1-4302-6695-2_4_Fig25_HTML.jpg

图 4-25。

Text beyond the optimum number of characters per line

防止这一点并不太难;您只需调整包装器,使其具有最大宽度,之后它将简单地使站点居中:

.wrapper{

padding-left: 15px;

padding-right: 15px;

max-width: 1200px;

margin: 0 auto;

}

现在,如果你在大视窗上检查站点,当视窗超过 1200 像素时,它会居中,如图 4-26 所示。

A978-1-4302-6695-2_4_Fig26_HTML.jpg

图 4-26。

Text now centering with larger viewport

改善电网

现在已经完成了第一个响应式网格,您已经了解了构建 CSS 网格的基本原则。如果你想更进一步,有很多方法可以改进网格,让你建立更好、更有用的站点。

第一种方法是增加列数。典型的网格使用 12 到 16 列,然而,为了简单起见,我只选择了 4 列。增加列数相对来说比较简单,只需使用以下公式计算每一列:

列宽= (100 /总列数)*列跨度

您可以选择使用 Nicolaj Kirkgaard Nielsen 的网格计算器,而不是手动计算。 4

改进网格的第二种方法是更好地控制不同视口大小的列数。您已经添加了媒体查询,以允许独立确定小型和大型设备的列数;但是,您可以通过添加针对中型设备的功能来添加更细粒度的控制。

摘要

流畅的布局是任何响应式网站的显著特征,因为它们允许网站利用任何可用的空间。正如本章所强调的,流体布局的这一特性对于响应式设计尤为重要,因为我们正在开发各种形状和大小的设备。

然而,流动的布局确实给我们作为开发者带来了一些额外的挑战,所以我们研究了一些可以遵循的原则,以确保我们能够优化我们的响应网站的可用性。此外,这些原则应该指导你能够快速有效地建立你的网站。

了解了流体设计之后,我解释了如何应用这些技术;特别是,我专注于构建一个流体响应 CSS 网格。这解释了如何将您已经学到的关于媒体查询的知识应用到流畅的布局中。

看了流畅的布局,学习了如何自己构建一个基本的网格,在下一章,你将学习更多关于现有的 CSS 网格和框架,我们可以用它们来构建我们的响应站点。

Footnotes 1

http://niarchive.org/trails/plantation-rewriting-the-story/

2

http://thomashawk.com/2013/05/flickr-users-uploading-71-more-photos-to-flickr-since-new-design-rolled-out.html

3

http://www.clholloway.co.za

4

http://gridcalculator.dk/

五、响应式设计的框架

我相信您能够理解,响应式开发确实给项目增加了额外的工作,在这种情况下,开发人员需要考虑网站在不同屏幕分辨率下的所有不同变化。在构建网站时,我们可以减少工作量的一种方法是使用响应式框架作为起点。

当看到第四章中讨论的流体设计时,我解释了 CSS 网格的基础,你学会了如何开发自己的网格。然而,在所提供的示例中,网格被大大简化了,开发一个更复杂的网格实际上可能相当耗时。值得庆幸的是,作为开发人员,我们有一个开放源代码的世界,本章将探索一些最流行的网格和框架。

在关于网格和框架的这一章中,我将解释的主题是:

Grid systems   CSS frameworks   What is best for a particular web site   Prototyping a site using CSS frameworks

网格系统

最简单的形式是,网格系统是一种结构,您可以在其中构建网站的布局。它由水平行和垂直列组成,包含网站的内容,可以跨越一列或多列。在构建网站时,网格系统可以提供很大的好处,允许你在构建中实现间距的一致性,从而提高网站的可用性,因为文本更容易理解。

在寻找开源网格系统时,您会注意到它们通常非常简单,只包含渲染网格所需的样式,而将如何构建站点的所有其他决定留给您自己。本章中解释的网格系统都是为响应而构建的,所以除了基本网格之外,它们还包括允许它们响应浏览器视窗宽度的媒体查询。

尽管 mobile first 响应式设计有很多好处,并且在 web 开发社区中也有接受它的驱动力,但是大多数独立的网格系统仍然是先构建桌面的。因此,在考虑网格系统时,首先要考虑它们是否是移动的,这一点很重要。

本章将介绍三种流行的移动优先响应网格。检查的网格系统有:

Fluidable ( http://fluidable.com/ )   CSS Smart Grid ( http://dryan.github.io/css-smart-grid/ )   csswizardry-grids ( http://csswizardry.com/csswizardry-grids/ )

流动的

Fluidable ( 5-1 显示其主页。

A978-1-4302-6695-2_5_Fig1_HTML.jpg

图 5-1。

The Fluidable home page

我问 Andri 他开发自己的网格系统背后的原因是什么,他解释如下:

I want a grid that can be configured for any number of columns, which is fluid (usage percentage), and most importantly, has slots of fixed width. I think this is the only way to make a grid. From the design point of view, shrinking your gutters is a big taboo. You want to be able to control when to change gutters. Then, when responsive design began to appear, it was no longer meaningful to use pixels. My last reason is that I really want something lightweight, which can be used for smaller projects, such as single pages and smaller websites, where it is unnecessary to use a mature framework.

在与 Andri 交谈后,很明显他在构建他的网格时试图实现我们在构建自己的网格时探索的许多内容,然而,在花了更多时间进行工作后,他已经实现了一个完整的 12 列网格(见图 5-2 )。

A978-1-4302-6695-2_5_Fig2_HTML.jpg

图 5-2。

Screenshot of the different grid arrangements shown on the Fluidable web site

在查看 Fluidable grid 是如何构建的过程中,可以看到它为每种不同的设备类型定义元素跨越多少列的能力,这意味着当使用这种技术时,可以轻松控制站点如何向下查看单个设备类型。实现这一点的方法是通过用于元素类名的命名约定:

.col-mb-x Defines how many columns it should span on mobile   .col-x Defines how many columns it should span on a tablet   .col-dt-x Defines how many columns it should span on a desktop

对于其中的每一个,x代表元素应该跨越的列数。在示例网格中使用了类似的方法,允许移动设备具有不同的网格布局,但是,它没有将平板电脑与桌面分开。Fluidable 没有这个限制,而是提供了三个不同的列类集,如上所列。我对他们实现的唯一不满是列类名的命名约定不一致,tablet 使用了col-x,这还不清楚。

从技术的角度来看,Fluidable 是使用 CSS 预处理器 LESS (leaner CSS [CSS 预处理器将在本书后面讨论])构建的,这意味着如果您通过更改变量的值来使用 LESS,也可以自定义您的网格。Fluidable 可使用五个变量进行配置,即@columns、@maxWidth、@gutterWidth、@ screenTablet 和@screenDesktop,这使得它更加灵活。

CSS 智能电网

下一个电网系统是丹尼尔·瑞恩( http://dryan.github.io/css-smart-grid/ )的 CSS 智能电网。Daniel 最初于 2011 年在 GitHub 上发布了 CSS 智能电网,在九次发布之后,在撰写本文时它已经是 4.0 版了。图 5-3 显示了 CSS 智能电网的主页和文档网站。

A978-1-4302-6695-2_5_Fig3_HTML.jpg

图 5-3。

The CSS Smart Grid home page

我联系了 Daniel,问他为什么选择开发自己的网格系统,而不是使用现有的网格。我创建的网格诞生于 2011 年夏天,是专门为奥巴马竞选而设计的。我们希望首先实现移动化,但当时并没有很多健壮的网格采用这种方法。然后我们遇到了 polyfilling @media 查询对我们来说是一个真正的问题,因为所有的 JavaScript 解决方案都使用 AJAX 来加载和解析 CSS。我们的 CSS 是在一个不支持 CORS 报头的 CDN 上提供的,所以这对我们来说是不可行的。我的最后一个要求是网格必须是轻量级和高性能的。

他提出的最有趣的一点是他过去在多填充媒体查询方面的问题,所以我将解释这是如何作为 CSS 智能网格的一部分进行设计的。

CSS 智能网格设计巧妙,无需 JavaScript polyfill 即可支持 Internet Explorer 8。这是通过使用条件注释将类添加到 HTML 标记来实现的,HTML 标记可用于向 Internet Explorer 8 显示默认的 960 网格。通过使用这种技术,仍然可以使用移动优先的方法来构建网格,并在不需要任何 JavaScript 的情况下为 Internet Explorer 8 提供桌面布局。

已经了解了是什么让这个网格脱颖而出,让我们看看实际的网格,看看网格本身如何与这里正在研究的其他网格系统相比较(见图 5-4 )。

A978-1-4302-6695-2_5_Fig4_HTML.jpg

图 5-4。

Screenshot of the different grid arrangements shown on the CSS Smart Grid site

CSS 智能网格中的列是通过添加类“columns”和附加类来定义的,以指定它应该跨越的列数(例如,一、二、三)。最多支持 12 列。或者,您也可以使用关键字来指定元素的宽度,它们是:

one-fourth: equal to 25 percent or three columns’ width   one-half: equal to 50 percent or six columns’ width   one-third: equal to 33.33 percent or four columns’ width   two-thirds: equal to 66.66 percent or eight columns’ width

下面是一个实际使用网格的例子。正如您将看到的,定义了一行和两列,每列的宽度都是使用上面讨论的关键字定义的:

<div class="row">

<div class="columns two-thirds">

<p>Lorem ipsum</p>

</div>

<div class="columns one-third">

<p>Lorem ipsum</p>

</div>

</div>

使用这些关键字为您选择如何标记列增加了额外的灵活性,但是,重要的是要注意,在 CSS 中,它的作用就像您简单地使用数字列类名一样。

网格本身是使用 SASS(语法上令人敬畏的样式表)CSS 预处理器构建的,对于 CSS 智能网格,Daniel 提供了将列宽作为“mixin”的选项,而不是使用 HTML 中的类。这样做的好处是 HTML 看起来不那么混乱,因为您不需要在标记中添加那么多的类。然而,这样做的缺点是,因为您是在 CSS 中定义宽度,所以您的元素只能在该特定宽度下工作。相反,如果您要在 HTML 中使用列宽类,您可以简单地通过更改类来更改元素的大小。在这方面,块本身将是一个流体宽度;然后 column 类会简单地给它一个定义的宽度。这使您的代码更加灵活,因为您可以通过简单地更改所使用的列宽类,在不同宽度的多个位置使用相同的代码块。

CSS wizardy-网格

下一个网格是 Harry Roberts 的 CSS wizardy-grids(http://csswizardry.com/csswizardry-grids/)。这是一个移动优先的响应网格,可以在 GitHub 上下载(见图 5-5 )。

A978-1-4302-6695-2_5_Fig5_HTML.jpg

图 5-5。

csswizardry-grids home page

我联系了 Harry Roberts,询问他开发 CSS wizard-grids 背后的原因:

I want a practical answer to a complex question; Most other solutions look complicated. 1

网格非常灵活,与前面讨论的网格相比还有一个额外的好处,就是它允许您定义列是居中还是无间距。这意味着,您可以通过使用一个类来简单地使列居中,而不是尝试均匀地偏移列来使其居中。网格还具有额外的灵活性,允许您在其他列中嵌套列(参见图 5-6 )。

A978-1-4302-6695-2_5_Fig6_HTML.jpg

图 5-6。

Screenshot of the different grid arrangements shown on the csswizardry-grid site

与 CSS 智能网格类似,这个网格系统使用一个类命名约定,将列宽称为分数,例如:

one-half   one-third, two thirds   one-quarter, two-quarters, three-quarters

这种类命名约定比大量网格系统使用的 span-X 方法更容易阅读。

同样,以类似于 CSS 智能网格的方式,csswizardry-grids 允许您使用 SASS(这是一个 CSS 预处理器,将在本书后面讨论),您可以简单地将列的规则扩展到您自己的 CSS 类选择器,这意味着如果您选择,您可以避免将额外的类添加到 HTML 本身。使用这种方法的优点和缺点与使用 CSS 智能电网的优点和缺点相同。

使用 csswizardry-grids 的一个主要缺点是,它不使用 floats,而是使用 inline 块将列一个接一个地放置,所以如果 HTML 中的列之间有空格,那么在呈现的页面中就会出现多余的空格。有两种常见的方法可以防止这种情况:在元素之间放置 HTML 注释,如下所示:

<div class="one-half">

lorem ipsum

</div><!--

--><div class="one-half">

lorem ipsum

</div>

或者简单地删除列之间的空格,如下所示:

<div class="one-half">

lorem ipsum

</div><div class="one-half">

lorem ipsum

</div>

虽然这是使用内联块的一个缺点,但是使用内联块比使用浮点有几个优点。它允许您使用dir HTML 属性颠倒内容的顺序。这可以添加到一个特定的元素中来定位站点的特定区域,也可以添加到 HTML 元素中来定位整个页面。HTML 属性有两个可能的值:ltr代表从左到右,而rtl代表从右到左的文本。图 5-7 显示了使用dir属性如何影响 CSS wizard-grids。

A978-1-4302-6695-2_5_Fig7_HTML.jpg

图 5-7。

Grid default alignment on the left, the text direction set to “right to left” on the right

使用dir属性有它自己的优点;首先,你可以按照可访问性和搜索引擎优化的重要性来排列你的内容,而不是简单地按照设计来组织你的内容。第二,如果你正在构建一个支持从右到左语言的多语言网站,你可以使用dir属性简单地反转布局。

对网格使用 inline block 的另一个好处是,元素自然具有正确的高度,并且您不必清除浮动。

CSS 框架

除了开源网格系统,还有各种各样的开源网格框架。CSS 框架是典型 CSS 网格系统的扩展,除了提供网格布局之外,它还提供标准的浏览器重置、版式和用户界面元素,您可以使用它们来构建响应性站点。虽然它被称为 CSS 框架,但重要的是要知道,这些 CSS 框架中常见的一些界面元素通常需要框架附带的 JavaScript。

有许多不同的 CSS 框架可用,但是,有两个真正被 web 开发社区所接受:Twitter Bootstrap 和 Zurb Foundation。

CSS 框架中常见的用户界面元素有:

Dropdowns   Button’s and button groups   Forms   Navbar   Breadcrumbs   Pagination   Labels   Badges   Page header   Thumbnails   Alerts   Progress bars   Media object   List group

通过提供广泛的组件选择,CSS framework 使开发人员的生活更加轻松,因为当他们需要一个公共元素时,他们可以简单地从现有的组件中进行选择,使他们能够将时间集中在站点的外观和感觉上,以及适应他们正在构建的站点的新的自定义组件上。

让我们看看几个最流行的 CSS 框架。

Twitter 引导

Twitter Bootstrap 是一个 CSS 框架,最初由 Mark Otto 和 Jacob Thornton 构建;然而,它已经成为 GitHub 上最受欢迎的项目,有近 600 名贡献者和超过 25,000 个分支。该框架也相当成熟,已经发布了 25 个版本,在撰写本文时已经发布到 3.2.0 版。

显然,Twitter Bootstrap 正在被大量的开发者使用,但是这并没有立即显示出一些使用 Twitter Bootstrap 的网站的规模。包括 Healthcare.gov、Virgin Active 和愤怒的小鸟星球大战 II 的 Tumblr 在内的品牌都在使用 Twitter Bootstrap,并且以非常不同的方式使用,这也表明了该框架是多么通用。

马克·奥托(Mark Otto)为 List Apart ( http://alistapart.com/article/building-twitter-bootstrap )写了一篇文章,谈到了 Twitter Bootstrap 开发背后的原因:

A year and a half ago, a small group of Twitter employees set out to improve our team's internal analysis and management tools. After some early meetings around this product, we set out with greater ambition to create a toolkit for anyone inside or outside Twitter. Therefore, we set out to build a system to help people like us build new projects on this basis, so Bootstrap came into being. 2

马克的这个更高的目标是试图建立一个每个人都可以使用的工具包,这个目标非常成功;Twitter Bootstrap 实现了 Twitter 团队的目标,即它是一个可用于各种项目的工具包,通过开源项目,它为更广泛的开发社区提供了这些好处。

当我们开始深入研究 Twitter Bootstrap 所提供的东西时,从基础开始是很重要的,Twitter Bootstrap 的基础是一个移动优先、响应迅速的网格系统。开箱即用的框架有一个 12 列的网格,分为超小型(移动)、小型(平板电脑/小型上网本)、中型(台式机/笔记本)和大型(大屏幕台式机),以便您能够根据不同的视窗大小完全调整站点。

Twitter Bootstrap 中一些最常用的组件如图 5-8 所示。

Navigation

A978-1-4302-6695-2_5_Fig8a_HTML.jpg

Buttons

A978-1-4302-6695-2_5_Fig8b_HTML.jpg

Forms

A978-1-4302-6695-2_5_Fig8c_HTML.jpg

Typography

A978-1-4302-6695-2_5_Fig8d_HTML.jpg

A978-1-4302-6695-2_5_Fig8_HTML.jpg

图 5-8。

Twitter Bootstrap components Thumbnails

这是 Twitter Bootstrap 附带的组件的一个小样本,为了探究它们,您应该看一看 www.getbootstrap.com 来全面了解 Twitter Bootstrap 组件提供了什么。

除了内置组件,web 开发社区还用第三方组件扩展了 Twitter Bootstrap,这些组件可以简单地添加进来。一些流行的附加组件示例如下:

Fuel UX: ( http://exacttarget.github.io/fuelux/ ) Fuel UX extends Bootstrap by adding additional lightweight JavaScript components.   Bootstrap Image Gallery: ( http://blueimp.github.io/Bootstrap-Image-Gallery ) This component adds the ability to have a gallery with a light box that can navigate through a series of images.   Bootstrap Application Wizard: ( http://www.panopta.com/2013/02/06/bootstrap-application-wizard/ ) The Bootstrap Application Wizard component allows you easily to make multipart forms with Bootstrap. These are shown inside a modal that shows above your page.

这种用其他开源组件扩展 Twitter Bootstrap 的能力使它非常强大。然而,与 jQuery 不同的是,jQuery 有一个用户定制插件的存储库,没有用于引导组件的存储库,这意味着你必须依靠像 Google 这样可靠的搜索引擎来找到这些组件。虽然这不是一个主要的缺点,但它会使找到推荐的组件变得更加困难。

作为开发人员,理解框架是如何构建的是很重要的,因为我们经常希望能够删除我们不使用的部分,这样他们就不会不必要地膨胀我们的站点。使用 Twitter Bootstrap,主项目的构建使用了更少的资源。然而,有一个官方的 SASS 端口,所以当涉及到您使用哪个版本时,您可以选择。我会在第七章单独讨论 CSS 预处理器。

如果你陷入困境,需要 Twitter Bootstrap 的帮助,你可以求助于一个大型的开发人员社区,很可能通过快速搜索,你会发现有人可能已经问过同样的问题,这意味着你可以快速解决你的任何问题。

Zurb 基金会

Zurb Foundation 是一个由 web 设计机构 Zurb 构建的 CSS 框架,于 2011 年 2.0 版首次成为开源。从这个初始版本开始,已经有了三个后续版本,其中 5.3.3 是撰写本文时的版本。

看看谁在使用 Zurb 基金会,我们可以看到许多大型网站都在使用它,包括 Dictionary.com、英国 HTC 在线商店和世界野生动物基金会。

Zurb Foundation 中的组件与 Twitter Bootstrap 中的组件非常相似。简单看一下其中的一些组件,就会发现两者提供的主要组件是相同的,如图 5-9 所示。

Navigation

A978-1-4302-6695-2_5_Fig9a_HTML.jpg

Buttons

A978-1-4302-6695-2_5_Fig9b_HTML.jpg

Forms

A978-1-4302-6695-2_5_Fig9c_HTML.jpg

Typography

A978-1-4302-6695-2_5_Fig9d_HTML.jpg

A978-1-4302-6695-2_5_Fig9_HTML.jpg

图 5-9。

Zurb Foundation components Thumbnails

如果你想看看 Zurb Foundation 真正与众不同的地方,那就是它的 Interchange Responsive 内容(或简称 Interchange)。简而言之,Interchange 允许您为特定的媒体查询将 HTML 的不同部分加载到页面中。这意味着,当您构建 HTML 时,您不需要在主页中包含所有视窗宽度的 HTML,而是专注于提供移动优先的 HTML,然后使用 Interchange 加载不同视窗的附加 HTML。此外,Interchange 可用于根据视窗宽度加载不同的图像,因此您可以在较小的视窗中加载较小的优化图像,在较大的视窗中加载较大的图像。

Zurb 基金会不仅仅是 Zurb 开发的开源项目。它还提供付费支持,帮助您充分利用该框架以及培训和资格鉴定。尽管并非所有项目都负担得起这个选项,但它通常被认为对一些公司环境有益。

什么最适合我的网站?

看了 CSS 网格系统和 CSS 网格框架之后,您现在应该对两者的优缺点很熟悉了。当你开始构建一个站点的时候,决定使用哪一个框架或者网格系统是很重要的,因为一旦你的构建有了进展,就很难改变了。

很难改变框架或网格系统的原因是,您已经承诺了代码结构和命名约定,这些都是与您选择的框架或网格一起出现的,因此改变可能意味着需要进行大量的重构。在框架的情况下,您可能也使用了一些用户界面组件,因此改变框架将涉及到寻找替代框架的等价物或在其他地方寻找替代物。

当要在使用 CSS 网格和 CSS 框架之间做出决定时,问自己是否想要预构建用户界面组件是一个简单的问题。您还应该看看除了接口组件之外还提供了什么;像用于排版的基本 CSS 这样的东西可以节省你大量的时间,你可能会发现这会让你使用 CSS 框架而不是 CSS 网格。

重要的是要记住,没有一个放之四海而皆准的解决方案,尽管您可能选择在一个网站上使用网格系统或网格框架,但您可能会发现对于另一个网站,您会做出不同的决定。

CSS 框架经常受到很多批评,也许这是你过去不喜欢的一个原因,那就是它们的文件大小。文件大小背后的原因是 CSS 框架包括许多不同的组件,每个组件都会增加框架的文件大小。为了能够充分利用您选择的框架,您应该尽可能地使用它们。然而,这并不是忽视框架的理由,因为你可以选择在项目中使用框架的哪些部分,你可以简单地删除任何未使用的 CSS 选项,手动或使用自动工具,如后处理器uncss(这将在第七章的中介绍)。

选择 CSS 网格

在决定了网格更适合您,并且您希望能够构建自己的所有组件之后,看看可用的选项是很重要的:

选择网格时,您需要问自己一些问题:

Do you feel comfortable working with the grid? It is important that you feel comfortable with the grid, even small things like how classes are named can be an annoyance if it does not fit with the way you normally code, so it is important that you are 100 percent comfortable with the grid, especially because you may be maintaining the site for a number of years.   Is the grid suitable for your site? Your grid of choice should work for your site. This is especially important if you already have had the site designed without a prior discussion about grids. This means if you want to use a grid, it needs to fit the number of columns for which the site has been designed. Some grid systems will allow you to configure the number of columns they use, which can aid you in trying to get a design to fit into a grid. A better approach is to have chosen the grid before the site is designed and discuss this with the designer so you know your grid is suitable.

选择 CSS 框架

在决定使用一个 CSS 框架后,确保你选择一个你乐意使用的框架是很重要的——记住你不太可能建立一个你不会再接触的网站,所以你需要做出一个你乐意的选择,因为你可能会在可预见的将来维护这个网站。

选择框架时,您需要问自己一些问题:

Do you like the grid the framework is built on? As previously discussed, the base of a CSS framework includes a grid system, so you have to be comfortable with the grid.   Are you happy with the selection of components that the framework comes with? The user interface components that the framework comes with are a core part of the framework, so it is important that you are happy with the selection. Although you can add your own, using the components that come with the framework will not only make your life easier it can also save you a significant amount of time.   Does the framework suit the way you work and the tools you use? Some frameworks might require you to use specific tools, and it is likely you already have a set of tools you know and love, so you will need to consider this when choosing a framework.

既不选择网格也不选择框架

也有一些 CSS 网格系统和 CSS 框架都不合适的情况,例如:

The design uses uneven column sizes.   The design has uneven margins in between the columns.   The width of the design is not easily divisible.   The site content is placed on the page in a irregular manor (an example of this could be a parallax site).

如果你的网站属于这些类别中的任何一个,你很可能会花费大量的时间试图让你的网站适应网格,而不是花时间在没有网格的情况下构建你的网站,但仍然遵循已经讨论过的移动优先的方法。

使用 CSS 框架构建网站原型

传统上,作为网站开发生命周期的一部分,用户经验丰富的设计师会将一系列静态线框放在一起,向客户、设计师和网站开发人员说明网站应该如何工作。连同解释功能的注释,这将是构建网站的基础。最近,已经开发了允许构建交互式线框的应用程序,然而,许多这些应用程序仍然非常局限于简单地将一系列静态图像与链接捆绑在一起。

这是传统网站开发之外的一个领域,使用 CSS 框架可以提供显著的改进。CSS 框架真正的亮点在于能够使用作为框架的一部分提供的组件快速构建网站原型。无需编写一行 CSS 代码,使用包含的组件快速模拟原型是非常容易的。

我们已经探索了一些不同的 CSS 框架,其中之一,Twitter Bootstrap,我们将用于模拟一个网站的原型。

首先,您需要为组件设置一个基本模板。为此,你需要包含 Twitter 的 CSS 框架,可以在 www.getbootstrap.com 下载。对于基本模板的 HTML,你只需要一些基本的 HTML,包括 jQuery、Twitter Bootstrap CSS 和 JavaScript。输入清单 5-1 中的代码。

清单 5-1。基本模板 HTML

<!DOCTYPE html>

<html>

<head>

<title>Prototype</title>

<meta name="viewport" content="width=device-width">

<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">

</head>

<body>

<script src="http://code.jquery.com/jquery-1.10.1.min.js

<script src="js/bootstrap.min.js"></script>

</body>

</html>

有了这些,现在可以开始用 Twitter 引导组件构建原型了。为此,您将从 Twitter Bootstrap 文档中提取模块的代码片段,并在适当的地方缩短它们。

您将包含的第一个元素是一个navbar。Twitter Bootstrap 文档中的navbar模块包括下拉框和搜索表单,然而,在本例中,您将包括一个简化版本,如清单 5-2 所示。

清单 5-2。原始资料改编自 http://getbootstrap.com/components/#nav 的文档

<header>

<nav class="navbar-default" role="navigation">

<div class="container">

<!-- Brand and toggle get grouped for better mobile display -->

<div class="navbar-header">

<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">

<span class="sr-only">Toggle navigation</span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

<a class="navbar-brand" href="#">Prototype</a>

</div>

<!-- Collect the nav links, forms, and other content for toggling -->

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">

<ul class="nav navbar-nav">

<li class="active"><a href="#">Link</a></li>

<li><a href="#">Link</a></li>

<li><a href="#">Link</a></li>

</ul>

</div><!-- /.navbar-collapse -->

</div>

</nav>

</header>

正如当你建立一个响应式网站时,你首先建立的是这个原型手机,所以第一站是在一个额外的小设备上测试导航,如图 5-10 所示。

A978-1-4302-6695-2_5_Fig10_HTML.jpg

图 5-10。

Testing the navigation on an extra small device

如果你现在在桌面浏览器中测试同样的导航,你会得到一个全幅导航,如图 5-11 所示。

A978-1-4302-6695-2_5_Fig11_HTML.jpg

图 5-11。

The navigation shown on a larger viewport, showing the full-sized navigation

无需编写任何 CSS 或 JavaScript,您现在就拥有了一个在移动设备上响应迅速、折叠自如的导航。

接下来,您要添加超大屏幕组件,它本质上是一个大的功能标题区域,有时被称为 hero。键入清单 5-3 中的代码来添加这个组件。

清单 5-3。原始资料改编自 http://getbootstrap.com/components/#jumbotron 的文档

<section class="jumbotron">

<div class="container">

<h1>Prototype</h1>

<p>This is our prototype</p>

<p><a href="#" class="btn btn-primary btn-lg" role="button">Learn more</a></p>

</div>

</section>

如果您现在在我们的超小型设备上再次测试,标题看起来更完整。通过简单地放入超大屏幕组件,你已经为页面添加了一个标题和一个主要的行动号召,如图 5-12 所示。

A978-1-4302-6695-2_5_Fig12_HTML.jpg

图 5-12。

The extra small viewport of the site featuring the jumbotron

当您查看原型现在如何显示在桌面上时,您可以看到 Twitter Bootstrap 超大屏幕组件以更大的版式响应更宽的视窗,如图 5-13 所示。

A978-1-4302-6695-2_5_Fig13_HTML.jpg

图 5-13。

The jumbotron viewed on a wide viewport

接下来,您将添加一些面板来展示您网站的产品。对于图像,您将使用一个名为lorempixel.com的服务,它将显示一个随机图像作为占位符。对于产品,使用缩略图组件,如清单 5-4 所示。

清单 5-4。原始资料改编自 http://getbootstrap.com/components/#thumbnails-custom-content 的文档

<section class="container">

<div class="row">

<div class="col-sm-4">

<div class="thumbnail">

<img src="http://lorempixel.com/400/200/

<div class="caption">

<h3>Product one</h3>

<p>...</p>

</div>

</div>

</div>

<div class="col-sm-4">

<div class="thumbnail">

<img src="http://lorempixel.com/400/200/

<div class="caption">

<h3>Product two</h3>

<p>...</p>

</div>

</div>

</div>

<div class="col-sm-4">

<div class="thumbnail">

<img src="http://lorempixel.com/400/200/

<div class="caption">

<h3>Product three</h3>

<p>...</p>

</div>

</div>

</div>

</div>

</section>

缩略图组件就绪后,您现在可以刷新浏览器,主页的原型现在看起来已经完成。为了让您能清楚地看到产品模块,我在图 5-14 中展示了这些模块在我们的超小型设备上的外观。

A978-1-4302-6695-2_5_Fig14_HTML.jpg

图 5-14。

The product panels viewed on a small device

同样,当你在桌面上查看原型时,你可以看到网站现在使用了三栏,而不是使用一栏来显示产品,如图 5-15 所示。

A978-1-4302-6695-2_5_Fig15_HTML.jpg

图 5-15。

Larger device showing the product panels alongside one another

之所以有三列,是因为您选择了使用列类col-sm-4,这意味着每一列跨越了组成网格的十二列中的四列(这是页面宽度的三分之一)。使用这些引导组件的好处是,您可以使用列类来定义它们在不同浏览器宽度下的外观。只需将类别更改为col-sm-3并添加一个额外的产品框,就会并排显示四个产品,如图 5-16 所示。

A978-1-4302-6695-2_5_Fig16_HTML.jpg

图 5-16。

Four product columns shown on larger device

正如刚才演示的那样,不需要编写任何 CSS,只需修改文档中的 HTML,使用 CSS 框架创建一个原型真的很容易。虽然这是一个非常简单的例子,但它确实突出了 CSS 框架必须提供的一些好处。构建原型比花时间创建线框有几个主要好处:

Those using the prototype can get a feel for how the site will work rather than relying on the notes attached to the wireframe.   By using a responsive CSS framework, your prototype is by default responsive, allowing you to show how the site will function on different devices.   Rather than starting from scratch when building the site, you might be able to build on top of the prototype.

按照这里的第 2 点,已经使用响应式 CSS 框架构建了这个原型,您已经能够构建完全响应的原型。这使得这个原型的用户可以快速看到他们应该如何期望最终的网站能够跨各种设备工作。

摘要

本章研究了 CSS 网格系统和 CSS 框架,特别关注在哪里可以使用它们,以及它们可以给你的响应式设计项目带来的好处。

本章考察了选择最适合您的项目的网格或框架的重要性,而不是将其建立在对特定框架的忠诚感上。最重要的方面是网格或框架相对于您的需求的灵活性。

CSS 框架特别有用的一个领域是当你想要建立一个网站的原型时,这一章探讨了如何通过简单地改编来自 Bootstrap 网站的 HTML 例子并把它们放在一起制作一个页面来快速建立一个网站的原型。这是非常强大的,因为这意味着你可以构建一个工作原型,然后利益相关者可以自己测试交互,而不是构建平面的用户体验图,然后解释各种交互。这个原型可能会成为构建整个网站的基础。

在下一章中,我们将探讨如何使一个现有的网站具有响应性。

Footnotes 1

哈里·罗伯茨,2014 年 1 月 17 日,推特@csswizardry。

2

马克·奥托,《与众不同的清单》, 2012。

六、改编现有网站

到目前为止,本书的章节已经讨论了如何从站点响应的角度出发,从头开始构建一个新站点。然而,从头开始构建可能并不总是一种选择,并且已经放弃了为移动使用构建单独的站点,很可能您会想要调整您现有的站点和代码库以做出响应。为此,有三个选项可供选择:

Adapt the current styles   Refactor the code to be mobile first   Do a full reskin

在这三个选项中,您选择的方法取决于您站点的具体情况,因为每个选项都有自己的优缺点。本章将通过一个例子来研究每一种不同的方法,这样你就可以清楚地看到修改一个现有的站点使之具有响应性所涉及的步骤,之后你就可以选择最适合你的项目的方法了。

在这一章中,我为一个虚构的公司制作了一个没有响应的站点,我称之为没有响应的设计公司,我将用它来演示将一个现有站点转换为有响应的站点的不同方法。它包含在本书网站的代码包中( www.apress.com )。你可能想在你的电脑上按照我提供的说明进行操作。

改编现有的样式和脚本

调整站点的三种方法中的第一种是保留现有的 HTML,并调整现有的样式和 JavaScript,以便它们通过添加媒体查询来响应设备视口。

这种方法是一种桌面优先的方法,因此您会优雅地降低较小设备的体验。降低网站内容质量的方法有很多,第一种方法是简单地隐藏不太重要的内容,并重新排列剩余的内容,使其适合较小设备的屏幕,这两种方法都可以单独使用 CSS 来实现。这种方法的好处是可以相对快速地完成,但是,您认为不太重要的内容可能对您的一个或多个用户很重要。第二种方法是使用 CSS 来调整内容,使其在网站上更好地工作,并在适当的时候使用 JavaScript 来改变用户与内容的交互方式。这两种方法的总体目标都应该是提供比用户在较小的设备上直接使用桌面站点更好的体验。

通过采用调整当前样式的方法,您可以保持现有的样式不变,而不是使用媒体查询来定位在网站开始崩溃的地方覆盖现有样式的样式。

这种方法带来的一个好处是,现有的代码库可能已经过浏览器测试。这反过来会减少你遇到尚未修复的浏览器特有错误的可能性。在不支持媒体查询的浏览器中,网站看起来与修改前一样。在浏览器支持媒体查询的情况下,你可以集中精力在你添加的新断点上测试站点,而不是在重新换肤过程中引入的错误。

这种方法的一个主要缺点是,您正在调整的原始桌面站点可能已经有了臃肿的代码库,遗留或冗余的代码导致了效率低下。通过调整一个已经臃肿的网站,您可能会使问题变得更糟,并且可能会因为在连接速度较慢的设备上加载更多代码而面临性能问题。在这种情况下,您可能希望在着手提高站点的响应能力之前减少这些低效问题。实现这一点的方法之一是移除任何未使用的 CSS 选择器,这些选择器可能是站点的旧版本遗留下来的。有很多工具可以用来实现这一点,其中之一是 Firefox 插件 Dust-Me Selectors ( https://addons.mozilla.org/en-US/firefox/addon/dust-me-selectors/ ),它将扫描您的网站并找到任何未使用的 CSS。

采用这种方法的另一个问题是,在许多情况下,浏览器仍然会在较小的设备上下载较大的图像。蒂姆·卡德勒克进行了测试,看看在什么情况下浏览器会下载图像( http://timkadlec.com/2012/04/media-query-asset-downloading-results/ )。在他测试的几乎所有浏览器中,他发现对于包含在页面中的图片。img 元素,浏览器将下载图像,即使 CSS 指定它不应该显示。此外,他还发现,如果背景图像被设置为 CSS 中的元素,并且媒体查询指定该元素不应被显示,则图像仍然会被下载。但是,有一种方法可以防止背景图片加载。在进一步的测试中,Tim 发现如果在想要隐藏的元素的父元素上使用display: none,背景图片将不会被下载。因此,如果你想隐藏背景图像,这是你需要采取的方法。

就像简单地更新一个站点一样,当修改现有的样式时,确保您保持现有的编码样式是很重要的。这样做的原因是样式必须保持一致,包括类名使用相同的命名约定,字体大小使用相同的单位。

了解了为网站调整现有 CSS 和 JavaScript 的优缺点后,让我们来看一个例子,看看在构建一个响应式网站时如何做到这一点。第一步是看看现有的网站,并确定到底需要改变什么。我已经为我虚构的公司示例提供了一个基本模板的截图,这样你就可以开始看到改进它的可能方法(见图 6-1 )。

A978-1-4302-6695-2_6_Fig1_HTML.jpg

图 6-1。

The fixed width site that will be make responsive

定义断点

在查看了我想要修改以提高响应性的原始站点之后,我需要决定一组断点,使其能够在各种设备上工作。对于这个网站,我将添加两个断点:第一个是针对平板电脑等小型设备,第二个是针对手机等超小型设备。

对于第一个断点,我想尝试缩小现有的网站,以便它能舒适地适合小型设备。我想添加一个断点,以便当现有的网站不再适合在视窗中,它可以切换到网站的较小版本。为了实现这一点,断点需要在水平滚动条由于站点太宽而被迫出现之前中断。为此,我需要考虑到网站的宽度加上垂直滚动条的宽度。不幸的是,滚动条的宽度可以根据包括操作系统和浏览器在内的许多因素而变化,所以我将计算站点宽度加上20px的断点。这意味着,对于这个特定的站点,该站点将在1000px有一个断点。当我在这个例子的后面提到这个断点时,我将把它称为小断点。此断点的代码是:

@media screen and (max-width: 1000px){

}

下一个断点需要以移动设备为目标。由于设备种类繁多,尺寸和屏幕分辨率各不相同,很难说平板电脑尺寸的界限和移动设备尺寸的界限。因此,在使用媒体查询确定将什么归类为移动设备时,您需要做出判断。一个很好的起点是查看您希望支持的一些平板电脑的纵向宽度。最容易找到这些信息的地方是 www.viewportsizes.com ,它允许你找到关于不同视窗宽度的信息。使用这个站点,您可以查看一些最常见的平板电脑的视窗宽度,以确定您想要将什么定义为移动断点。你可能会看到苹果的 iPad,其纵向视窗宽度为768px,以及微软的 Surface,其纵向视窗宽度也为768px。因此,如果768px对于平板电脑来说很常见,您可以将移动设备的断点设置为767px,因为这比平板电脑少 1px。当我在这个例子的后面提到这个断点时,我将把它称为额外的小断点。此断点的代码是:

@media screen and (max-width: 767px){

}

排印

现有的排版对于大多数设备来说是一个很好的尺寸,但是,对于超小型设备来说就有点太大了,所以你需要给你的标题添加一些样式。为此,您需要查看已经使用的基本字体大小,以便能够正确计算字体大小值。这方面的代码应该是:

@media screen and (max-width: 767px){

h1{

font-size: 22px;

font-size: 1.571em;

}

h2{

font-size: 18px;

font-size: 1.286em;

}

}

包装材质

调整了字体大小后,现在需要调整包装的宽度,使其不会超出设备视窗的边界。为此,您需要决定您的站点应该如何跨越您定义的不同断点。第一个断点是小断点,它被定义为具有1000pxmax-width。如前所述,这是针对现有桌面站点和移动站点之间的设备的断点。对于这个断点,您可以将站点包装器的宽度设置为710px,因为站点需要在从768px1000px的视口中工作,这包括滚动条。你还需要考虑 20px 左右的填充。此断点的代码应该是:

@media screen and (max-width: 1000px){

.wrapper{

width: 710px;

}

}

当你下到额外的小视口时,让站点有一个流动的宽度是有意义的。这有几个原因,首先是在这些较小的设备上,您想要尝试利用浏览器视窗宽度的每个像素来显示内容,并且您不能承受在边缘周围有多余的空间。第二个原因是在320px767px之间,用于查看网站的屏幕宽度有很大的变化。因此,您需要尝试为所有这些宽度提供优化的体验。为了使您能够在这些额外的小视口设备上制作包装流体,您需要将宽度设置为 100%。设置后,您会注意到包装器仍然比视口宽。这是因为您对包装应用了填充。为移动站点保留这种填充是有意义的,因此与其删除它,不如使用box-sizing属性允许你在 100%宽度中包含填充。在第四章的中,我讨论了对通用(*) CSS 选择器应用盒子大小;不幸的是,因为这是一个现有的代码库,所以在全球范围内应用是不实际的,因为您可能会破坏一些现有的样式。这就是为什么当你修改现有的 CSS 时,你应该根据具体情况来调整盒子的大小。超小视口包装器的最终样式是:

@media screen and (max-width: 767px){

.wrapper{

width: 100%;

-moz-box-sizing: border-box;

-webkit-box-sizing: border-box;

box-sizing: border-box;

}

}

(电视机的)超大屏幕

如果在我进行的过程中,你和我一起测试过你的浏览器,你会注意到超大屏幕目前正在扩展到网站的包装之外。如果您查看大屏幕的现有 CSS 来了解为什么会发生这种情况,您将会看到大屏幕的宽度被设置为940px。显然,媒体查询需要根据不同设备上的大屏幕显示效果进行调整。

对于这个小断点,您只需要调整大屏幕的大小,使其适合包装器。因为包装器是710px宽,您需要使用以下代码调整大屏幕以匹配710px宽:

@media screen and (max-width: 1000px){

.jumbotron{

width: 710px;

}

}

然而,当遇到额外的小断点时,你需要做更彻底的改变,因为大屏幕的内容将不再适合现有的布局。您需要调整布局,以便在一个额外的小设备上观看时,大屏幕上的内容可以堆叠起来。要实现这一点,第一步是设置大屏幕,使其填充包装纸,让包装纸填充任一侧:

@media screen and (max-width: 767px){

.jumbotron{

width: 100%;

height: auto;

text-align: center;

padding: 20px 0;

}

}

如果你看一看大屏幕的原始位置,你会看到里面的元素都被绝对定位,这意味着在改变宽度和改变高度为auto之后,这个网站看起来真的很破。这可以通过重置元素的位置和宽度来解决:

@media screen and (max-width: 767px){

.jumbotron img,

.jumbotron p,

.jumbotron h2,

.jumbotron .roundal,

.jumbotron .roundal span{

position: static;

left: auto;

right: auto;

bottom: auto;

top: auto;

width: auto;

}

}

让超大屏幕在这些超小型设备上看起来合适的最后一步是调整标题文本,使字体大小更适合移动设备。此外,为了使标题文本不会靠近大屏幕的边缘,您可以在标题文本中添加填充:

@media screen and (max-width: 767px){

.jumbotron h2{

font-size: 22px;

font-size: 1.375em;

padding: 10px 20px;

}

}

如果你现在看着超小型设备上的大屏幕,你会看到信息更加清晰(见图 6-2 )。

A978-1-4302-6695-2_6_Fig2_HTML.jpg

图 6-2。

Our slimmed down site for the Jumbotron

制品

下一步是调整产品,使它们在各种断点处看起来都很棒。首先,你应该从决定它们在小型设备上的外观开始。要确定这一点,您需要考虑现有内容是否适合现有结构,或者您是否需要调整布局。调整布局可能很简单,只需将三列布局改为两列布局,也可能意味着将布局改为宽度可变的单列。然而,在这个例子中,在这个小的断点上,通过简单地将三个产品元素的宽度改为223px,现有的布局和内容可以很好地适应现有的三列结构:

@media screen and (max-width: 1000px){

.product{

width: 223px;

}

}

调整小型设备以继续使用三列,尽管宽度更小,下一步是研究如何调整产品元素以在超小型设备上工作。此时,视口变得太小,无法继续在三列中显示产品,当使用额外的小断点时,您使用的是一个流体宽度,因此有必要更改产品元素,使它们不再在列中,而是相互堆叠。为此,您需要将产品元素的宽度调整为 100%。此外,您可能希望确保产品之间有一个清晰的分隔线,这可以通过填充和边框来实现:

@media screen and (max-width: 767px){

.product{

width: 100%;

padding: 20px 0px;

border-top: 1px solid #ccc;

}

}

添加了填充和边框后,第一个产品现在在顶部有了一个额外的边框和额外的填充,因此您可以使用:first-child选择器来定位第一个产品并移除多余的填充和边框:

@media screen and (max-width: 767px){

.product:first-child{

border-top: 0px;

padding-top: 0px;

}

}

类似地,在最后一件产品下面显示了额外的多余填充,因此可以通过使用:last-child选择器瞄准最后一件产品并移除多余的填充来移除:

@media screen and (max-width: 767px){

.product:last-child{

padding-bottom: 0px;

}

}

如果你现在看看这些产品是如何堆叠的,你会注意到它们工作得相当好,如图 6-3 所示。

A978-1-4302-6695-2_6_Fig3_HTML.jpg

图 6-3。

Slimmed down product panels

结论

在对站点进行适应性修改后,您现在拥有了一个可以在各种不同设备上运行的站点。以这种方式修改网站既有好处也有坏处,我现在将讨论它们,这样当涉及到您自己的网站时,您可以决定简单地修改您现有的网站是否适合您的需要。

当你调整大屏幕时,你可能已经注意到了一个主要问题,那就是当你把网站降级到更小的设备上时,你有时需要覆盖大量的样式来为移动设备提供良好的体验。虽然在这个例子中它相对简单,但是一个更大的站点可能需要你做更多的样式覆盖。无论您是先在移动设备上工作,还是先在桌面上工作,您总是会覆盖上一个断点的一些样式。通常一个移动优先的站点是建立在每个断点已经存在的基础上的,但是像这样一个桌面优先的站点,有时必须将值重置回它们的浏览器默认值会使代码库更加臃肿。

在这个例子中,简单地修改站点的好处是桌面站点的样式不需要改变,这意味着焦点可以是正在添加的新断点。这不仅节省了开发时间,还有助于简化测试,因为我们知道,在大多数情况下,我们发现的错误会被引入到我们的媒体查询中。

重构

改造现有网站的第二种方法是首先重构现有的代码库,使其具有可移动性。这里的好处是,你不必从头开始的风格。您还可以对网站设计进行调整,并在重构过程中删除多余的代码。

定义断点

作为站点重构的第一步,您需要在面向大型设备的媒体查询中包装当前用于样式化站点的现有样式,例如站点已经支持的桌面浏览器。要为此选择合适的媒体查询,请查看网站的现有样式以确定其宽度。如果你看看我们正在制作的示例站点的代码,你会看到主包装器有940px宽,加上左右两边的20px填充,总共有980px宽。考虑到滚动条,因此您将设置媒体查询来检查最小宽度1024px:

@media screen and (min-width: 1024px){

}

使用这个媒体查询来包装现有的样式,没有样式被应用到宽度小于1024px的任何视口。从这一点开始,我将把这个断点称为中间断点。

您还希望能够调整站点,以在比站点当前支持的更大的视口尺寸上工作,在这种情况下,您需要添加一个针对这些更大视口的额外断点:

@media screen and (min-width: 1200px){

}

除了已经定义的较大媒体查询之外,您可能还想为小型设备(如平板电脑)添加一个额外的媒体查询。此媒体查询将使您能够利用平板设备提供的较大视窗,为此,您将把 iPad 的较小宽度作为这些小型设备的突破点:

@media screen and (min-width: 768px){

}

重构现有样式

定义了断点之后,现在可以重构现有的样式了。您已经将所有现有的样式移动到一个断点中,因此这意味着在小于1024px的设备上,将不会显示任何样式,如图 6-4 所示。

A978-1-4302-6695-2_6_Fig4_HTML.jpg

图 6-4。

No styles are shown on smaller devices

此时,您可以开始迁移当前针对较大州的样式,以便它们可以在全球范围内使用。您可以很容易地找出全球可用的样式,这些样式应用了不管断点如何都会使用的字体和通用间距。

在示例站点的情况下,站点使用默认的浏览器字体,所以不需要修改字体。该站点不对元素类型应用任何通用间距,但是有一个用于清除浮动的clearfix助手类。这对于在媒体查询之外可用是有意义的,因为在较小的设备上也需要它。

移动任何通用样式后,下一步是查看站点的不同部分,看看可以在哪里获取现有样式并将其应用到较小的视口,在此过程中重构它们。

让我们从站点的主包装器开始这个重构过程。应用的填充和边距可以很容易地从媒体查询中提取出来。其他样式特定于较大的视口尺寸,因此让我们将它们留在原来的位置:

.wrapper{

padding: 0 20px 20px;

margin: 0 auto;

}

页眉

下一步是查看标题样式,看看哪些样式可以用于较小的设备。在本例中,应用于页眉的现有样式只是间距和底部边框。将它们从媒体查询中移出是有意义的,这样它们就可以被其他视口大小访问。将要移动的样式如下所示:

.global-header{

margin: 0 0 20px;

border-bottom: 1px solid #ccc;

}

如果您在浏览器中查看标题,您会看到它现在已经应用了一些基本样式。但是,由于在此阶段没有对字体大小进行任何处理,标题文本显得相当大(如图 6-5 所示)。

A978-1-4302-6695-2_6_Fig5_HTML.jpg

图 6-5。

The site header, with no font sizes applied

为了纠正这一点,让我们向h1元素添加样式,以设置字体大小并使文本居中对齐:

h1{

font-size: 1.4em;

text-align: center;

}

现在,当你在浏览器中查看时,字体大小更适合较小的视窗,如图 6-6 所示。

A978-1-4302-6695-2_6_Fig6_HTML.jpg

图 6-6。

The site header, with the font size and alignment applied

(电视机的)超大屏幕

这个网站的下一个需要关注的元素是大屏幕。大屏幕的原始 CSS 有 35 行长,大部分只适用于较大的视窗宽度,所以仔细看看什么是合适的是很重要的。为此,我在下面包含了原始的超大屏幕 CSS,并突出显示了您可能想要用作较小设备上的超大屏幕的起点的行:

.jumbotron{

position: relative;

background: #f4a156;

width: 940px;

height: 250px;

margin-bottom: 20px;

}

.jumbotron img{

position: absolute;

left: 0px;

top: 0px;

}

.jumbotron h2{

position: absolute;

left: 260px;

top: 20px;

font-size: 3em;

width: 300px;

margin: 0px;

}

.jumbotron .roundal{

position: absolute;

right: 0px;

bottom: 0px;

}

.jumbotron .roundal span{

position: absolute;

right: 20px;

bottom: 20px;

font-size: 2em;

}

从上面选取 CSS 中突出显示的行,然后就剩下生成的 CSS:

.jumbotron{

background: #f4a156;

margin-bottom: 20px;

}

.jumbotron h2{

margin: 0px;

}

如果你现在在浏览器中查看大屏幕,你会看到你已经有了样式的开始,如图 6-7 所示。

A978-1-4302-6695-2_6_Fig7_HTML.jpg

图 6-7。

The Jumbotron with this base CSS in place

您将会注意到,大屏幕的橙色背景并没有像您预期的那样在整个视窗中延伸。这是因为您使用的包装器在站点的两侧应用了一致的间距。为了使您能够保持一致的间距,但将背景扩展为全幅,您可以使用负边距。要在大屏幕上添加一致的间距,只需应用填充即可。此外,您可能希望文本居中对齐。这显示在下面的 CSS 中:

.jumbotron{

background: #f4a156;

margin:  0 -20px 20px;

padding:  20px;

text-align: center;

}

字体大小也特别大。这是因为第二级标题的字体大小尚未针对较小的视窗进行设置,因此它们现在应该设置为适当的大小。既然一级标题设置为 1.4em,那么二级标题也应该设置为1.3em。您也不希望在超大屏幕中的第二级标题上方有额外的间距,所以专门为这个超大屏幕将页边距顶部设置为0px

h2{

font-size: 1.3em;

}

.jumbotron h2{

margin-top: 0px;

}

在这个位置,大屏幕现在看起来是完整的,如图 6-8 所示。

A978-1-4302-6695-2_6_Fig8_HTML.jpg

图 6-8。

The complete Jumbotron

产品面板

下一步是为产品面板构建样式。用于较大视口中这些面板的大多数样式也可以用于较小视口中这些面板的样式。和以前一样,我突出显示了下面的 CSS 代码行,这些代码行可用于简单地将其移出媒体查询:

.product{

float: left;

width: 300px;

padding-left: 20px;

text-align: center;

}

.product:first-child{

padding-left: 0px;

}

.product .roundal{

width: 100px;

height: 75px;

margin: 0 auto;

background: #f4a156;

border-radius: 50px;

padding-top: 25px;

}

.product .roundal span{

display: block;

font-size: 1.6rem;

}

.product ul{

padding: 0px;

}

.product li{

list-style: none;

font-size: 0.8rem;

color: #333;

}

.product a{

font-size: 0.9rem;

color: #000;

text-decoration: none;

display: inline-block;

background: #ccc;

padding: 5px 10px;

border-radius: 2px;

}

.product a:hover, .product a:focus, .product a:active{

background: #999;

color: #eee;

}

.product *:first-child{

margin-top: 0px;

}

.product *:last-child{

margin-bottom: 0px;

}

将突出显示的 CSS 移到媒体查询之外后,现在需要添加一些额外的填充和边框来完成产品面板。为了使边框两边都有间距,在每个产品面板的顶部和底部添加 20px 的填充。然后将边框应用到产品面板的底部:

.product{

text-align: center;

padding: 20px 0px;

border-bottom: 1px solid #ccc;

}

这样,确保你在第一个面板上没有太多的空间。仅移除第一个产品面板的衬垫,这将使用:first-child选择器进行定位:

.product:first-child{

padding-top: 0px;

}

准备好之后,看看产品面板在浏览器中的样子,如图 6-9 所示。

A978-1-4302-6695-2_6_Fig9_HTML.jpg

图 6-9。

The product panels on a smaller device

你需要看的 CSS 的最后一部分是页脚。让我们从查看当前应用于更大的媒体查询中的页脚的 CSS 开始。我突出显示了 CSS 中有意义的行,以便将其置于媒体查询之外,这样它就可以应用于额外的小视窗及以上:

.global-footer{

margin: 20px 0;

border-top: 1px solid #ccc;

}

.global-footer p{

text-align: center;

font-size: 0.8rem;

}

不需要为页脚再写任何 CSS,完成的页脚如图 6-10 所示。

A978-1-4302-6695-2_6_Fig10_HTML.jpg

图 6-10。

The finished footer on extra small devices

针对不同的断点

在这一点上,该网站在超小型设备上运行良好,但我们还没有考虑它在小型或大型设备上的外观。也有可能是因为添加了新的样式,我们打破了原来的(中等)布局。让我们先把浏览器的宽度增加到一个小设备的大小,看看网站看起来怎么样,如图 6-11 所示。

A978-1-4302-6695-2_6_Fig11_HTML.jpg

图 6-11。

The site shown on a small device

现在,在小型设备上看到网站的样子后,您会注意到堆叠的产品面板周围有很多空间,可以通过将产品一个接一个地排成一行来更好地利用这些空间。这与原始无响应站点的行为方式相同,但是,您可以应用一个流体宽度,而不是为产品面板设置一个固定的宽度。因为这里需要三列,所以可以将宽度设置为33.33%。要让面板并排放置,请应用一个浮动并移除元素顶部的填充。最终的代码如下所示:

.product{

width: 33.33%;

padding-top: 0px;

float: left;

}

如果你在你的浏览器中检查这个,面板现在应该是并排的,如图 6-12 所示。

A978-1-4302-6695-2_6_Fig12_HTML.jpg

图 6-12。

The product panels on a small device

如果您继续增加视窗大小,直到达到中等视窗宽度,您会注意到站点的页眉和超大屏幕都被破坏了。这是因为您应用于早期断点的样式。我已经在图 6-13 中展示了割台和大屏幕是如何损坏的。

A978-1-4302-6695-2_6_Fig13_HTML.jpg

图 6-13。

The medium breakpoint with the broken Jumbotron

作为重构的一部分,您可以选择对网站的设计进行修改,在本例中,您将对页眉和大屏幕都进行修改。第一步,看表头;目前 CSS 已经将样式应用于h1使其向左浮动,CSS 是:

.global-header h1{

float: left;

}

如果不想让它向左浮动,可以去掉这个样式。然而,你想增加字体大小,但是你用和移动站点一样的方式应用字体大小是有意义的,移动站点直接在h1上。更改字体大小的新样式如下所示:

h1{

font-size: 2rem;

}

有了这个地方,下一步是看大屏幕。首先,让我们看看当前应用于大屏幕的 CSS。第一部分是包含大屏幕主元素的 CSS,这个元素上的类是jumbotron。现有的 CSS 是:

.jumbotron{

position: relative;

width: 940px;

height: 250px;

}

正如你会注意到的,大屏幕有一个定义为940px宽的值集。因为我们希望大屏幕始终与包装一样宽,所以您可以简单地删除宽度,这样 CSS 现在看起来就像:

.jumbotron{

position: relative;

height: 250px;

}

下一步是定位图像。虽然您可以重构 HTML 来使用基本的网格并使用它来构建超大屏幕,但为了简单起见,让我们简单地使用绝对定位来定位超大屏幕的内容。现有样式当前定位图像,因此它绝对位于左上角:

.jumbotron img{

position: absolute;

left: 0px;

top: 0px;

}

但是我们想把图像放在大屏幕中心的左边。为此,您应该将选择器的 left 属性的值设置为 50%。然后,您可以使用与图像宽度相等的负边距将其向左拉。您还想将顶部向下凸起,给它一点额外的空间,所以让我们将顶部设置为 20px:

.jumbotron img{

position: absolute;

left: 50%;

top: 20px;

margin-left: -250px;

}

下一步是设计大屏幕的标题。它目前的样式绝对定位它,所以它坐在旁边的图像的原始位置。因为我们改变了图像的位置,所以您还需要调整标题的位置,使其仍然位于图像旁边。要做到这一点,请将标题放在大屏幕中心的右侧。当前的 CSS 是:

.jumbotron h2{

position: absolute;

left: 260px;

top: 20px;

font-size: 3rem;

width: 300px;

margin: 0px;

}

要获得想要的效果,只需更改左侧,使其为 50%:

.jumbotron h2{

position: absolute;

left: 50%;

top: 20px;

font-size: 3rem;

width: 300px;

margin: 0px;

}

下一步是在大屏幕上定位定价。您希望它出现在标题文本的下方,所以让我们使用相同的技术来使文本偏离中心。我们已经在使用绝对定位,所以你只需要改变位置。现有的 CSS 是:

.jumbotron .roundal{

position: absolute;

right: 0px;

bottom: 0px;

}

首先,将左侧的定位设置更改为 50%,然后将底部设置更改为50px。这将使文本直接位于标题下方。更新位置后,增加字体大小使其更加突出,在这种情况下,让我们将字体大小增加到2rem:

.jumbotron .roundal{

position: absolute;

left: 50%;

bottom: 50px;

width: 300px;

font-size: 2rem;

}

最后,您可以简单地删除下面的样式,这些样式最初定位在roundel内的跨度,但是不再需要:

.jumbotron .roundal span{

position: absolute;

right: 20px;

bottom: 20px;

font-size: 2rem;

}

调整了标题样式和大屏幕样式后,让我们在浏览器中查看它们,如图 6-14 所示。

A978-1-4302-6695-2_6_Fig14_HTML.jpg

图 6-14。

The completed header and footer

除了页眉和大屏幕,网站的页脚也有问题,因为它有一个额外的边框显示,如图 6-15 所示。

A978-1-4302-6695-2_6_Fig15_HTML.jpg

图 6-15。

The double border issue on the medium breakpoint

这是因为产品的底部添加了边框,但是较大的视窗的页脚顶部已经添加了边框。要解决此问题,只需从样式表中删除以下样式:

.global-footer{

margin: 20px 0;

border-top: 1px solid #ccc;

}

现在,如果你在浏览器中再次查看,你会看到这条线已经被删除,如图 6-16 所示。

A978-1-4302-6695-2_6_Fig16_HTML.jpg

图 6-16。

The fixed footer for the medium breakpoint

这完成了中等断点,但是作为使站点响应的一部分,您还想利用较大显示器上的可用空间。前面我讨论了用于实现这一点的媒体查询,但是现在让我们来看看调整站点所需的 CSS。

因为到目前为止构建的所有东西在固定宽度的包装器中都是流动的,所以只需改变这个包装器的宽度,就可以为更大的视口调整站点。在某些情况下,您可能想通过做更多的事情来利用额外的空间,但是对于这个例子,让我们简单地调整包装器。当大断点在1200px处中断时,您会希望将包装器的宽度设置为1170px宽,以便为滚动条留出空间。为此,只需将以下 CSS 放入大型媒体查询中:

.wrapper{

width: 1170px;

}

加载完成后的页面,视口大于等于1200px将显示新的更宽的站点,如图 6-17 所示。

A978-1-4302-6695-2_6_Fig17_HTML.jpg

图 6-17。

The completed refactored site shown on a large viewport

全 Reskin

修改现有网站的第三种方法是保留现有的大部分 HTML,但是抛弃现有的 CSS,重新开始。

选择使用完整的 reskin 方法有好处也有坏处,关键的好处是,通过重新启动 CSS 并根据需要重构 HTML,您可以克服在站点的生命周期中可能已经蔓延到 CSS 中的任何低效问题。此外,因为您将替换现有的 CSS,所以您可以采用移动优先、渐进增强的方法,首先为较小的移动设备构建站点,然后根据设备功能集和视口大小增强站点。

准备

作为一个完整的 reskin,第一步是清除原始样式,看看还剩下什么。通过删除核心样式,你只剩下简单的内容。如果你在浏览器中查看,你会看到文档的轮廓非常清晰,如图 6-18 所示。

A978-1-4302-6695-2_6_Fig18_HTML.jpg

图 6-18。

The initial site with the original styles removed

这仍然很容易理解的部分原因是正确地使用了标题标签,所以尽管网站只有浏览器默认样式,内容仍然有一个强大的、清晰的标题层次结构。当你看你自己的网站时,你可能会注意到你的一些标题似乎没有遵循一个适当的层次结构。这是一个很好的机会来看看如何重构你的标题,让它们为用户提供一个合适的页面层次结构。为了演示不恰当的层次结构可能会是什么样子,我整理了一个例子,如图 6-19 所示。

A978-1-4302-6695-2_6_Fig19_HTML.jpg

图 6-19。

An example of a page with an improper hierarchy

页面遵循清晰的层次结构非常重要,原因有很多,其中最重要的原因是这样可以让用户更容易访问页面,尤其是那些使用屏幕阅读器的用户,他们可能会依赖页面的层次结构来浏览内容。标题层次结构很重要的另一个原因是,它使我们能够轻松地应用与正确的标题元素相匹配的样式。

常规样式

在开始核心的编码之前,让我们首先改变元素的默认盒子模型。这是通过使用box-sizing CSS3 属性实现的,该属性在第四章中介绍过,并使用 CSS 通用选择器(*)将其应用于所有元素:

* {

-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */

-moz-box-sizing: border-box;    /* Firefox, other Gecko */

box-sizing: border-box;         /* Opera/IE 8+ */

}

通过将值设置为border-box,您将移动元素内部的边框和填充,允许您使用宽度的百分比值,同时仍然使用填充的px值。早先当我们修改 CSS 的时候,由于破坏现有风格的可能性,我们不能很容易地将这种技术应用到 CSS 中。当重新划分一个站点时,你不会有这个问题,因为你可以基于你选择实现的盒子模型来构建所有的样式。

定义断点

重要的是要计划在什么时候使用媒体查询来定义断点。因为您首先构建的是移动站点,所以您会根据设备视口的宽度在每个断点处逐步增强站点。因此,每个断点都应该尝试建立在前一个断点之上。首先构建 mobile 意味着不包含在任何媒体查询中的基本 CSS 是针对超小型设备的 CSS。考虑到这一点,您将需要定义三个断点,每个断点使用一个合理的最小宽度值。

要定义的第一个断点是最小宽度768px。这将允许您以从768px开始的视口宽度瞄准设备。这种选择背后的原因是,这通常是纵向平板电脑的起点,随着平板电脑获得更大的屏幕和更宽的视窗,您可以利用这一点来增强网站。当后面提到这个断点时,我称它为小断点:

@media screen and (min-width: 768px){

}

要定义的第二个断点是最小宽度1000px,目的是针对大量用户使用的非常流行的 1024×800 屏幕分辨率。虽然前面的断点可以假设大多数用户将使用某种平板电脑,但该断点在平板电脑和台式机之间共享,因此不能用于做出这种假设。以后提到这个断点时,我称之为中断点:

@media screen and (min-width: 1000px){

}

要定义的最后一个断点是最小宽度1200px。正如本书前面所讨论的,随着更大显示器的增长,利用额外的可用空间是有意义的。当稍后提到这个断点时,我将它称为大断点:

@media screen and (min-width: 1200px){

}

排印

一旦你对断点满意了,让我们开始对排版应用合理的默认尺寸。第一步是设置页面的基本字体大小。为此,一个合理的默认大小是14px,它在 HTML 标签上设置为百分比:

html{

font-size: 87.5%;

}

设置默认字体大小会影响网站上的所有文本,但是,您可能希望为标题设置自己的自定义字体大小:

h1{

font-size: 22px;

font-size: 1.571rem;

}

h2{

font-size: 18px;

font-size: 1.286rem;

}

您可能已经注意到,对于h1h2选择器,我已经定义了两次字体大小属性,第一次使用像素值,第二次声明使用rem中的值。这样做的原因是您想要使用rem(也称为相对 em),因为它们是相对于默认字体大小的。然而,这并不是所有的浏览器都支持的,所以作为一个后备,我用像素来定义字体大小。这种方法的缺点是,在不支持rem的旧浏览器中,基于文本大小的缩放将不起作用,然而,整页缩放仍将起作用,这意味着使用这些旧浏览器的人仍然可以使用该网站。

定义了移动 CSS 之后,让我们为更大的设备增加字体大小。您将通过向小型媒体查询添加增加的字体大小来实现这一点,因为您正在使用min-width,所以它将应用于所有大于它的断点:

@media screen and (min-width: 767px){

h1{

font-size: 28px;

font-size: 2rem;

}

h2{

font-size: 24px;

font-size: 1.714rem;

}

}

包装材质

您可能已经知道,HTML 包含一个带有类wrapperdiv元素,它封装了站点的 HTML。在被替换的站点的原始 CSS 中,这被用来定义站点的宽度并使其在页面上居中。但是,对于移动设备,您不希望设置宽度,因为您希望包装器是流动的。但是,您确实希望对站点的两侧应用一致的间距,因此您可以为此使用包装器。这可以通过在包装的左右两边增加空白来分隔页面来实现:

.wrapper{

margin: 0 20px;

}

在包装器中添加了页边距后,网站内容的左右两边将会有20px页边距。尽管流体宽度非常适用于超小型设备,但您希望在小型设备上设置固定的宽度,并且还希望在视口中居中固定的宽度设计,这可以通过使用边距来实现:

@media screen and (min-width: 767px){

.wrapper{

width: 740px;

margin: 0px auto;

}

}

为小型设备设置了宽度后,当到达中等断点时,您会发现为小型断点设置的宽度留下了大量的可用空间。因此,在这些中等尺寸的显示器上使站点更宽是有意义的:

@media screen and (min-width: 1000px){

.wrapper{

width: 980px;

}

}

最后,您希望能够利用更大显示器提供的额外空间,因此让我们再次增加包装的宽度。在这种情况下,我选择使用最小宽度的断点1200px。这是为了确保浏览器不显示水平滚动条。让我们将站点的宽度设置为1170px,以允许浏览器的滚动条:

@media screen and (min-width: 1200px){

.wrapper{

width: 1170px;

}

}

页眉

看了网站的版式和包装,让我们继续设计网站的标题。在超小型设备上,我们希望在标题底部有一个跨越视口宽度的边框。然而,当包装器的边距设置为20px时,页眉自然不会跨越 100%的视口。要解决这个问题,让我们使用负边距将标题的宽度拉至视窗的全宽。然后,您可以使用填充将此间距添加到页眉的内部,然后将边框添加到页眉的底部:

.global-header{

border-bottom: 1px solid #ccc;

margin: 0 -20px;

padding: 0 20px;

}

对于较大的视口,您可能需要删除这些额外的间距:

@media screen and (min-width: 767px){

.global-header{

margin: 0px;

padding: 0px;

}

.global-header h1{

text-align: left;

}

}

(电视机的)超大屏幕

标题的正下方是大屏幕,用来突出显示我们虚构的公司 responsive design inc .想要推广的最新产品。这个面板应该很突出,所以让我们添加一个橙色的背景色,同时拉出大屏幕,使其适合视窗的整个宽度:

.jumbotron{

background: #f4a156;

margin: 0 -20px;

padding: 20px;

text-align: center;

}

接下来,让我们为大屏幕中显示的标题元素添加一些样式,使其顶部没有任何边距:

.jumbotron h2{

margin-top: 0px;

}

最后,让我们来设计一下大屏幕上突出显示的这款产品的价格。为此,让我们简单地增加字体大小,使其更加突出:

.jumbotron .roundal{

font-size: 18px;

font-size: 1.286rem;

}

如果你现在在浏览器中加载网站,你将能够看到风格化的大屏幕,如图 6-20 所示。

A978-1-4302-6695-2_6_Fig20_HTML.jpg

图 6-20。

The newly styled Jumbotron

现在让我们将它应用到更大的设备上。要达到想要的效果,还是用绝对定位吧。为此,您需要将高度和元素设置为position: relative:

.jumbotron{

margin: 0px;

height: 290px;

position: relative;

}

下一步是定位图像。您希望大屏幕中的所有内容都在中央,所以让我们将其设置为左侧 50%,然后使用margin-left添加偏移。在这种情况下,图像的宽度为 250 像素,所以您希望它偏离中心 20 个像素,在这种情况下,偏移量为-270 像素:

.jumbotron img{

position: absolute;

left: 50%;

margin-left: -270px;

}

同样,标题需要居中,然后使用margin-left偏离中心。尽管图像向左偏离中心,但文本将向右偏离中心。让我们也增加字体大小:

.jumbotron h2{

position: absolute;

left: 50%;

margin-left: 20px;

width: 250px;

font-size: 42px;

font-size: 3rem;

text-align: left;

}

最后,我们来定位价格。这应该与标题对齐,但更靠近大屏幕的底部:

.jumbotron .roundal{

position: absolute;

left: 50%;

margin-left: 20px;

bottom: 40px;

font-size: 28px;

font-size: 2rem;

}

现在,如果你看看大屏幕是如何出现在更大的设备上的,它看起来会如图 6-21 所示。

A978-1-4302-6695-2_6_Fig21_HTML.jpg

图 6-21。

The Jumbotron as seen on larger devices

制品

产品是这个网站的核心部分,所以用一种清晰简洁的方式向用户展示它们是很重要的。已经有 HTML 列出了产品名称和规格的价格,但是,您需要对其进行样式化,以便它在超小型设备上运行良好。在这些设备上,产品可以堆叠在一起,这样用户就可以上下滚动来查看不同的产品。由于每个产品都是一个div,这是一个块级元素,所以每个产品自然都是全幅堆叠的。但是运用风格来分隔产品也很重要。最后,您可以将文本居中对齐,以与页眉和大屏幕的样式保持一致:

.product{

text-align: center;

margin: 20px 0;

border-bottom: 1px solid #ccc;

padding-bottom: 20px;

}

确定了产品之后,下一步是设计产品的价格。为此,您可以使用一个roundal。为了实现这一点,让我们将roundal的样式设为100px宽,高度将由70px的高度和30px的顶部填充组成。圆形效果将通过给它一个橙色的背景色和50px的边框半径来实现。最后,这将使用边距居中对齐。为了以更大的字体显示价格,将 span to be display 块(强制换行)的字体大小设置为1.6rem:

.product .roundal{

width: 100px;

height: 100px;

margin: 0 auto;

background: #f4a156;

border-radius: 50px;

padding-top: 30px;

}

.product .roundal span{

display: block;

font-size: 1.6rem;

}

接下来,您可能想要添加产品的样式规范。首先,让我们删除浏览器应用于列表的默认填充,然后删除项目符号,因为在列出规范时不需要它们:

.product ul{

padding: 0px;

}

.product li{

list-style: none;

}

下一步是设计 read-more 链接。这应该是一个按钮,所以让我们来设计一下:

.product a{

color: #000;

text-decoration: none;

display: inline-block;

background: #ccc;

padding: 0px 10px;

border-radius: 2px;

line-height: 40px;

}

声明了按钮之后,您还想为按钮添加悬停、聚焦和活动状态。虽然您的第一想法可能是小型触摸屏设备不具备悬停功能,因为它们通常都有触摸屏,但您需要确保覆盖 CSS 中的所有内容,因为您无法知道用户是否正在使用触摸设备:

.product a:hover, .product a:focus, .product a:active{

background: #999;

color: #eee;

}

最后,您会注意到产品周围出现了额外的间距。这是由 product 元素中第一个和最后一个元素的边距造成的。您可以使用first-childlast-child选择器快速移除它们。这样做而不是直接设置元素样式的原因是,元素的顺序可能会改变,或者可能会添加新元素,无论元素是什么,CSS 都仍然适用:

.product > *:first-child{

margin-top: 0px;

}

.product > *:last-child{

margin-bottom: 0px;

}

将产品构建成堆栈后,您会发现无论浏览器的宽度如何,它们都工作得很好(见图 6-22 ):

A978-1-4302-6695-2_6_Fig22_HTML.jpg

图 6-22。

Product stack works well regardless of browser

尽管流体宽度产品在所有视口尺寸下都工作良好,但在较大的视口中,您会注意到有很多空间。产品和其中元素的流动性意味着当指定产品元素的宽度时,它和其中的内容将继续以指定的宽度工作。这意味着,在较大的视窗上,您可以通过将产品放入栏中来利用额外的空间。要实现这一点,只需将产品元素的宽度设置为33.333%,这样产品就占据了可用宽度的三分之一:

@media screen and (min-width: 767px){

.product{

float: left;

width: 33.333%;

}

}

然后你可以看到这些列并排出现在浏览器中(见图 6-23 )。

A978-1-4302-6695-2_6_Fig23_HTML.jpg

图 6-23。

Products line up side by side in browser

如果你用百分比来表示产品元素的宽度,它们会随着站点宽度的增加而增加。

页脚

reskin 的最后一个样式是页脚。这真的很简单,因为产品已经在产品和页脚之间提供了一个分隔符,所以你需要做的就是居中对齐文本,并在下面添加一点点间距:

.global-footer{

text-align: center;

padding-bottom: 10px;

}

这些样式不需要任何媒体查询来扩展,因为它们在包装器中是流动的。

结论

使用新的 CSS 重新设计站点后,您已经了解了如何首先构建移动版本,并在移动版本的基础上使用每个断点来构建一个可以简化的站点。

您还了解到,通过在超小型设备上构建流畅的元素,您可以在不同的断点对它们应用固定的宽度,然后您只需要应用最少的额外样式。

重要的是要记住,当你重新构建 CSS 时,你不一定需要一个完全相同的站点作为最终结果。对于你自己的网站,在你着手一个 reskin 之前,最好和你的设计师和 UX 团队讨论他们是否想对网站做一些调整,因为 reskin 是做这件事的最好时机。

摘要

这一章解释了如何修改一个现有的站点来响应。您学习了三种不同的技术来使站点具有响应性。

第一种使站点具有响应性的方法是修改现有的 CSS。改编现有 CSS 的主要好处是,它比执行完整的 reskin 要快得多。通过修改现有的 CSS,您已经有了一个开始的基础。另一方面,这可能是一个缺点,因为它会导致代码变得更加臃肿,因为您可能必须用浏览器默认行为来覆盖您已经设置的值。一个非常简单的例子就是绝对定位,在较小的设备上,您不需要以某种方式改变元素的定位方式,甚至经常删除已经应用的绝对定位。

第二种使网站具有响应性的方法是重构现有的代码库,使其既具有响应性,又是从移动优先的角度构建的。这里的关键好处是,你可以先获得构建一个移动站点的性能和可用性的好处,而不需要做一个完整的皮肤。主要的缺点是,在一个大型网站上,您可能会面临一个大的代码库需要重构,并且很难隔离不再需要的遗留代码,因为可能有一个页面嵌入到使用它的网站深处。

使站点具有响应性的最后一种方法是对站点进行全面的重新换肤。做一个完整的网站皮肤的主要好处是,它使你能够从移动优先的角度来构建,使用渐进式增强来构建网站,这可以帮助代码库更加精简。使用这种以移动设备为先的方式来构建网站,可以让你在逐步增强网站的同时添加更多的样式。

前几章已经强调了使用移动优先的方法来构建网站的好处,所以修改现有网站的首选方法是做一个完整的 reskin,删除原始 CSS 并开始使用移动优先的方法。然而,如果时间是一个问题,你可能会发现你需要简单地修改现有的风格,虽然不理想,但你会实现一个可用的响应站点。如果你发现你还有一点时间,你可以考虑优化你的站点来减少桌面优先构建的一些缺点。

虽然这一章关注的是使网站具有响应性的 CSS 方面,但是你并不局限于此。如果有必要的话,你可以重构你的 HTML 的一部分,使你能够更好地适应你的网站。此外,如果站点的功能会受益,您可以编写响应式 JavaScript 来改变站点不同元素的工作方式。

下一章将着眼于如何改进我们在构建一个响应式网站时使用的工具和遵循的工作流程。

七、工具和工作流程

随着响应式设计的发展,开发一个网站所需的时间也在增加;原因是在开发网站的过程中有更多的变量需要考虑。考虑到这一点,找到优化我们工作流程的方法以使我们能够在其他开发领域节省时间变得越来越重要。随着响应式设计的增长,前端工具也在增长,这使我们能够更有效地编写代码。

本章将探讨:

Knowing your command line   Version control with Git   CSS preprocessors   Bower   Grunt   Gulp   Scaffolding   Other tools   Workflow

了解您的命令行

一开始,你的操作系统的命令行界面可能非常吓人,但是,它也可能非常强大。学习如何使用命令行有很多好处:

A large number of web development tools now reside as command-line utilities.   It allows you to do batch file processing, such as the renaming of files, which may not be achievable using the normal graphical user interface (GUI).   The same interface is used when you secure shell (SSH) into servers, so the skills are transferable if you later want to manage your own hosting server.

考虑到这些好处,让我们看看命令行,这样您就可以在您的网站开发中更多地利用它。

准备命令行

在我开始讨论命令行之前,您必须准备好一些基本工具。这在 Mac OS X 和 Windows 之间略有不同,所以我为每一个都提供了单独的说明。

Mac OS X

在第二章中,我讨论了安装 Xcode 来允许你使用 iOS 模拟器。Xcode 对前端开发人员有用的另一部分是命令行工具。安装命令行工具最简单的方法是从苹果开发者网站 https://developer.apple.com/downloads/index.action 下载。你需要使用苹果开发者账户登录,你可以免费注册,然后为你的特定操作系统下载最新的“命令行工具”。下载完成后,您可以简单地运行安装程序来加载工具。

安装完成后,您可以安装 Homebrew 软件包管理器,这是一个命令行工具,允许您安装 Node.js 等软件包。要安装 Homebrew,您需要运行以下命令:

ruby -e "$(curl -fsSLhttps://raw.github.com/Homebrew/homebrew/go/install

然后命令行会引导你完成家酿的安装。

Windows 操作系统

要在 Windows 上准备命令行工具,您需要安装 Git 和 Bash。这些可以从 http://msysgit.github.io/ 一起下载。下载 msysgit 包时,它会建议你下载最新版本。

在安装过程中,您可以使用默认安装选项。当询问您希望使用哪种安装方法时,选择“仅使用 Git Bash”安装完成后,您只需单击 Finish 即可。您将使用 Git Bash 作为命令行界面,而不是使用内置的 Windows 命令行。要打开 Git Bash,只需在开始菜单的 Git 文件夹中找到它。

命令您的命令行

确保命令行设置正确后,您现在可以了解如何使用命令行的一些基础知识。命令行为广泛的计算机功能提供了一个简单的界面,从简单的创建文件夹到递归地改变文件权限。

遍历目录

在命令行中工作时,您可能希望执行的一个基本操作是在目录之间导航并查看目录内容。您可以使用两个主要命令来实现这一点:lscd

限位开关(Limit Switch)

ls命令将允许您列出当前目录中的所有文件和目录。

激光唱片

cd命令允许你改变一个目录。你可以传递一个绝对路径(以/开头),例如/Users/jonathan/,或者你可以传递一个与你当前文件夹相关的路径,例如Users/jonathan

此外,还可以使用cd ..导航到父目录,或者使用倍数导航到多个父目录,比如../../

简单的文件操作

您还需要能够使用命令行操作文件,并且能够创建、复制、移动和删除目录和文件。

创建目录

mkdir命令允许你在当前目录下创建一个新目录。这是通过输入mkdir dirname实现的。如果您想为名为project_name的项目创建一个文件夹,您可以运行命令:

mkdir project_name

触控

有时,当您设置目录结构时,您可能希望在目录中创建占位符文件。不必打开文本编辑器并创建每个文件,您可以使用touch命令简单地创建它们。要创建名为index.html的空文件,只需使用命令:

touch index.html

丙酸纤维素

cp命令允许您复制现有文件。要使用该命令,您只需提供原始文件的相对或绝对路径,后跟您要将它复制到的位置的路径。使用该命令的语法如下所示:

cp original-file-path new-file-path

例如,如果您想要将文件复制到当前文件夹的子目录中。在这种情况下,您只需提供原始文件的路径以及要将其复制到的位置:

cp hello.jpg img/hello.jpg

平均变化

mv命令与cp命令的相似之处在于,您只需提供原始文件的相对或绝对路径,然后是您想要将它移动到的位置的路径。使用该命令的语法如下所示:

mv original-file-path new-file-path

例如,如果您想要将文件移动到当前文件夹的子目录中。在本例中,您只需提供原始文件的路径以及要将它移动到的位置。

mv hello.jpg img/hello.jpg

空间

如果你想删除你已经创建的文件,你可以使用rm命令。使用rm命令的语法是:

rm file-to-be-removed-path

因此,如果您想从 images 目录中删除文件hello.jpg,您可以使用以下命令:

rm img/hello.jpg

Note

rm命令不会将文件放入回收站,而是将它从系统中永久删除。

其他有用的命令

除了已经介绍的核心命令之外,在使用命令行时,还有一些额外的命令会很有用。

日本首藤

sudo命令用于以系统超级用户(通常称为 root)的身份运行任何命令,目的是如果您想要修改当前用户没有权限的文件,您可以使用sudo来完成。使用sudo命令的一个例子是删除一个您自己无权删除的文件,命令行是:

sudo rm protectedFile.jpg

cat命令允许你读取文件的内容。要使用cat命令,您可以运行该命令,然后运行您想要输出的文件:

cat error.log

除了允许您简单地读取文件,它还可以用来连接文件。这是通过运行cat,后跟一个您想要连接的文件名列表来实现的:

cat file1 file2 file3

在上面的示例中,连接的文件只是输出到屏幕上,但是,您可以通过使用大于号(>)后跟要输出的文件名来选择将其输出到文件中:

cat file1 file2 file3 > concatfile

尾巴

tail命令允许您简单地将文件的最后几行输出到命令行。要使用tail命令,您可以运行该命令,后跟您想要读取的文件名:

tail error.log

您也可以使用-n标志后跟您想要读取的行数来选择您想要查看的行数:

tail –n 5 error.log

进一步的文件

通过在您想要了解的命令前使用man命令,可以直接在您的终端中查看关于这些命令的更多文档,例如man rm

工具先决条件

在开始安装和使用这些工具之前,您需要安装它们所依赖的一些先决条件。您将在这里探索的工具需要的两个先决条件是 Ruby 和 Node.js。

红宝石

Ruby 是一种动态的、面向对象的编程语言,可用于多种用途。我们在这里对它感兴趣的原因是,它被你将在本章中探索的少量工具所使用。

装置

Mac OS X

Mac OS X 附带了一个版本的 Ruby。这将是您在本章示例中使用的版本。你可以选择安装一个像 RVM 这样的工具来升级到 Ruby 的新版本(关于这个的信息可以在 http://rvm.io 找到)。

Windows 操作系统

要在 Windows 上安装 Ruby,可以使用位于 http://rubyinstaller.org/ 的 RubyInstaller 包。这是一个自包含的 Windows 安装程序,它将安装 Ruby 语言以及执行环境和文档。

在安装过程中,你会被要求输入 Ruby 的目的地。在这个阶段,您需要选中复选框“将 Ruby 可执行文件添加到您的路径中”

Node.js

最重要的先决条件之一是 Node.js,它是 JavaScript 的服务器端实现,带有服务器端 API。这是本书示例中使用的许多工具所需要的,包括 Bower、Grunt 和 Serve。

装置

在 Windows 和 Mac OS X 上安装 Node.js 是非常不同的,所以我将分别详细说明每个步骤。

Mac OS X

要在 Mac OS X 上安装 Node.js,您将使用名为 Homebrew 的 Mac OS X 软件包管理器。安装相对简单,您只需打开终端并运行命令:

brew install node

这将为您下载并安装 Node.js。以这种方式使用 Homebrew 安装 Node.js 的好处是,您可以使用以下命令轻松更新到较新的版本:

brew update node

要测试 Node.js 是否已安装,只需安装以下工具之一。

Windows 操作系统

Node.js for Windows 可以通过 Windows installer 轻松安装,可从 http://nodejs.org/download/ 获得。

使用 Git 进行版本控制

Git ( http://git-scm.com/ )是版本控制系统(VCS),在大量开源项目中使用,使开发人员能够跟踪已经发生的变化。当您最初设置命令行时,您安装了 Git 源代码控制管理工具,因为它是您将使用的一些其他工具的先决条件。

使用 Git 的核心概念之一是它是一个分布式 VCS,这意味着在本地您将拥有一个存储库的副本,您可以使用它来执行所有主要的 VCS 操作,如提交、分支和合并。您可以继续简单地在本地使用 Git 来管理项目代码,但是,强大之处在于能够将您的代码从本地存储库推到远程存储库,您可以使用它来与其他开发人员协作。当您想要共享本地存储库中的代码时,您可以将本地提交推送到远程存储库。类似地,当您想要获得其他开发人员所做的最新变更时,您可以将它们从共享存储库中下载到您自己的存储库中。

如果你以前使用过像 Subversion (SVN)这样的集中版本控制系统,你会注意到这是非常不同的。然而,这种方法的好处是,即使没有互联网连接,您仍然可以提交,这允许您继续定期提交。

为了开始学习如何使用 Git VCS,让我们看看如何在命令行上使用它的基础知识。

初始化存储库

要在本地机器上初始化新的存储库,只需运行以下命令:

git init

这就产生了一个隐藏的。git 目录,Git 将使用它来管理目录中文件的版本。此时,存储库仍然是空的,因为没有任何文件添加到其中。

添加文件

当使用 Git 时,您需要告诉它您希望它对哪些文件进行版本控制。这是通过使用git add命令实现的。要添加单个文件,您只需运行命令git add以及您想要添加的文件名。添加 JavaScript 文件的一个例子是:

git add scripts/main.js

尽管以这种方式添加单个文件很容易,但是您可能需要添加大量文件,尤其是在进行初始提交时。这就是git add .发挥作用的地方,它告诉 Git 将当前目录下的所有文件添加到存储库中。

做出承诺

一旦添加了文件,就可以进行初始提交了。你可以通过使用git commit命令来完成。您可以使用-m标志来设置提交消息;因此,该命令将类似于:

git commit -m 'initial commit'

在您完成这个初始提交之后,您可以继续使用git commit来提交任何更改的文件。

添加遥控器

现在,您已经添加了一些文件并进行第一次提交,您可能希望将这些更改推送到远程存储库进行共享。要添加新的存储库,您需要使用git remote add命令,提供存储库的名称和 URL。这方面的一个例子是:

git remote add origin git@github.com:jonathan-fielding/repo.git

推动提交

要将提交推送到远程服务器,可以使用git push命令。第一次推送到远程服务器时,您需要设置刚刚添加的远程服务器的上游,为此您使用了-u标志。您将用于推送到远程服务器的命令是:

git push -u origin master

后续的推送可以简单地通过单独使用git push命令来实现。

拉动更改

使用git pull命令可以从远程服务器获取更改。

克隆存储库

到目前为止,您一直在开发自己的存储库;但是,在某些时候,您可能需要克隆一个远程存储库。这是通过使用后跟存储库 URL 的git clone命令来实现的。克隆 HTML5 样板文件库的一个例子是:

git clone git@github.com:h5bp/html5-boilerplate.git

克隆的存储库与本地初始化的存储库的工作方式完全相同。您可以添加新文件、删除文件、提交新文件,如果您对存储库有写权限,还可以提交您的更改。

CSS 预处理程序

CSS 预处理器背后的核心概念是,它是一种可以编译成普通 CSS 的语言。这个想法是,通过添加新的特性来扩展 CSS,您可以编写更少的代码。CSS 预处理器的一些主要特性是:

Variables   Nesting   Import external files   Extend existing styles   Mixins

CSS 预处理器已经变得非常流行,因此,预处理器的一些特性已经被改编以提交给 W3C。这里的想法是变量、混合和嵌套都有可能成为 CSS 规范未来版本的特征。

在我讨论各个预处理程序之前,让我们先来看看它们的核心特性以及它们如何工作的例子。下面的例子是使用 Sass 编写的,但是,这些是适用于大多数 CSS 预处理程序的核心概念。

变量

在 CSS 中,如果你想在多个地方使用一个值,你必须把它硬编码到那些地方。如果您稍后想要更新该值,您将需要遍历并更新每个地方的值。这可能很耗时,而且很容易出错,错过您想要更新的事件。CSS 预处理程序通过允许您将值定义为可以在整个代码中使用的变量来缓解这种情况。通过更新变量,它将更新所有使用该变量的地方,使它不容易出错。如何在 CSS 预处理器中使用变量的一个例子是使用蓝色。在本例中,您希望将颜色存储为一个变量,在这里您将把它命名为$blue。当需要使用变量时,只需将变量放在通常放置属性值的位置:

$blue: #3bbfce;

p{

color: $blue;

}

嵌套

CSS 预处理程序的另一个特性是,它们允许你以一种允许你写更少代码的方式嵌套你的 CSS。举一个普通 CSS 的例子:

.class-name{

background: #000;

}

.class-name p{

color: #fff;

}

.class-name p span{

color: #eee;

}

如果您要使用 CSS 预处理器来重写,您可以选择嵌套 CSS 选择器,就像在 HTML 中嵌套元素一样。这意味着您可以编写更少的代码,因为您不再需要编写长的选择器。为使用 CSS 预处理程序而重写的 CSS 示例如下:

.class-name{

background: #000;

p{

color: #fff;

span{

color: #eee;

}

}

}

您会注意到您已经在类选择器中嵌套了p选择器,并且在这个选择器中有span。编译后,生成的 CSS 将与原始 CSS 相同;但是,您用更简洁的方式编写了这段代码。另外,如果你以后想把class-name改成another-class-name,你只需要在一个地方更新它:

.another-class-name{

background: #000;

p{

color: #fff;

span{

color: #eee;

}

}

}

导入

当编写一个普通的 CSS 文档时,对于一个大型网站来说,即使 CSS 文档是以一种优化的方式编写的,它变得相当长也是正常的。响应式设计增加了这个问题,因为我们现在将媒体查询添加到我们的 CSS 中。

使用 CSS 预处理器可以让您将代码分割成单独的文件,从而避免处理大文件。这样做有几个主要的好处,第一个是可以更容易地管理较小的文件,第二个是如果在源代码控制中出现冲突,它们更容易解决。

CSS 预处理程序允许你通过实现一个叫做 partials 的概念来分割文件,partials 是一个单独的文件,里面有一小部分你的代码。为了防止分部被单独编译,您需要用下划线(_)作为前缀来命名您的分部,这告诉预处理器它是一个分部,只能作为另一个样式表的一部分来编译。

通常你会有一个主文件用来导入你的部分;在这里,您可以使用@import将一部分导入到文档中:

@import "utilities/_reset.scss";

@import "utilities/_typography.scss";

当使用 CSS 预处理器进行编译时,部分代码被内联到编译后的 CSS 文档中。

扩展

针对特定元素编写了一些样式后,您可能会发现在站点的其他地方也有类似的元素。与其重新声明 CSS 规则集或将类名添加到现有的选择器中,您可以做的只是使用预处理器扩展现有的样式。为此,首先要定义第一个元素的样式;在本例中,您将为一个简单的按钮定义样式:

.button{

display: inline-block;

background: #ccc;

border-radius: 10px;

padding: 5px 50px;

}

然后,您可以为新元素定义样式;在这种情况下,您将定义一个填充较少的小按钮。要扩展一个现有的元素,您可以使用@extend,后跟您正在扩展的选择器:

.button-sml{

@extend .button;

padding: 5px 20px;

}

混入类

CSS 预处理程序最强大的特性是定义混合的能力。mixin 是一个可重用的代码块,一旦定义,就可以在 CSS 中的任何地方使用。它允许您声明代码,您可以将变量应用到代码中,以输出所需的结果。有几个地方使用 mixin 可以使编写 CSS 变得更容易。让我们来看几个这样的例子。

加前缀

第一个例子展示了如何使用 mixin 解决浏览器前缀的问题。在本书的前面我提到过,当新的 CSS 特性在浏览器中实现时,它们通常以浏览器前缀为前缀。因为您想要支持所有的浏览器,所以您需要确保您的代码输出所有正确的浏览器前缀,为此您可以使用 mixin。要编写一个能够为 CSS 属性border-radius生成前缀代码的 mixin,您需要编写一个接受一个参数(border-radius的值)的 mixin,并编写属性的每个前缀版本,其值设置为传递给 mixin 的参数:

@mixin border-radius($argument) {

-webkit-border-radius: $argument;

-moz-border-radius: $argument;

-o-border-radius: $argument;

border-radius: $argument;

}

如果你现在想要使用border-radius mixin,你可以使用@include border-radius(value),其中(value)等于你想要设置的border-radius:

@include border-radius(10px);

快捷指令

你可能想使用 mixin 的另一个领域是写快捷方式。一个例子是 mixin,它获取用于定位元素的值,然后输出 CSS。虽然这是一个简单的例子,但它展示了编写一个 mixin 来处理您可能经常编写的 CSS 输出是多么容易:

@mixin position($top, $right, $bottom, $left) {

top: $top;

right: $right;

bottom: $bottom;

left: $left;

}

功能

在一些 CSS 预处理器中,除了 mixins,还有函数。虽然 mixin 和函数之间有相似之处,但关键的区别是 mixin 将直接输出 CSS,而函数将返回一个值,然后您自己输出该值。

使用函数而不是 mixin 的一个例子是当你想计算一个值的时候。在本例中,您可能希望返回计算的结果,然后在其他地方使用它。您甚至可以使用 mixin 中的函数来执行您需要的计算。

这种计算的一个简单例子是,您希望将像素字体大小转换为 rems。这方面的一个例子是:

@function px-to-rem($font-size){

$baseline-px: 16 //This is the base font size used on our HTML element

$baseline-rem: $baseline-px / 1rem * 1;

@return $font-size / $baseline-rem

}

要使用这个函数,您只需调用它,传递您想要的字体大小:

p{

font-size: px-to-rem(12px);

}

不同的预处理程序可用

在探索了 CSS 预处理器的一些关键原理之后,让我们来看看几个流行的预处理器。我将解释为什么它们应该成为您的工具集的一部分,以及在构建一个响应式站点时它们将如何节省您的时间。

Sass +指南针

Sass ( http://sass-lang.com/ )是语法上令人敬畏的样式表的缩写。我之前解释了 CSS 预处理器通常会包含的一些不同特性,您会很高兴地了解到 Sass 包含了所有这些特性。Sass 也是许多开源项目选择的 CSS 预处理器,包括本书前面讨论的 Foundation。此外,甚至还有一个 Sass 版本的 Twitter Bootstrap。

Compass 是位于 Sass 之上的一个附加框架,为它增加了额外的功能。这种功能的例子包括混音和生成精灵的能力。

有各种各样的工具可以与 Sass 和 Compass 一起使用来编译代码,所以让我们从使用命令行工具开始。

装置

Sass 和 Compass 都是作为 Ruby gems 安装的。要安装它们,只需运行:

gem update --system

gem install compass

安装 Compass 时,它也会安装 Sass,因为它是一个依赖项。

使用

Sass 语法有两种风格:第一种是 Sassy CSS,它是 CSS3 语法的超集,第二种是缩进语法。我将把重点放在 Sassy CSS 语法上,因为它不仅比缩进语法更受欢迎,而且从该语言的版本 3 开始,它已经成为 Sassy 的主要语法。

因为在这里的例子中您将使用带有 Compass 的 Sass,所以让我们从使用 Compass 来设置这个项目开始。为此,您将使用compass create命令,该命令将创建项目目录以及基本的 Sass 文件和编译的 CSS。对于这个例子,您将告诉命令项目的名称是sass_project:

compass create sass_project

在运行上面的命令时,Compass 将为项目创建一个新文件夹,其中包含文件。此外,它还会给出如何将编译好的 CSS 添加到 HTML 文档中的说明(见图 7-1 )。

A978-1-4302-6695-2_7_Fig1_HTML.jpg

图 7-1。

Creating Sass project with Compass, with instructions to add to HTML highlighted

在这个阶段,您仍然在刚刚创建的项目的父目录中,因此您将使用cd命令移动到这个项目的目录中:

cd sass_project

现在您已经在目录中,可以编译 Sass 了。要检查编译是否正常工作,只需运行以下命令:

compass compile

命令行会告诉你编译成功了。

在命令行中设置了项目之后,您还需要编写自己的代码。让我们打开文本编辑器中生成的 screen.scss 文件 Compass。打开文件后,您会发现以下六行 Sass:

/* Welcome to Compass.

* In this file you should write your main styles. (or centralize your imports)

* Import this file using the following HTML or equivalent:

* <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */

@import "compass/reset";

这段代码所做的是将默认浏览器重置导入到样式表的顶部。如果您想查看编译后的代码,只需查看样式表目录中的 screen.css 文件。

能够只使用一行代码添加 CSS 浏览器重置对于完整的站点构建非常有帮助;然而,对于这个例子,您将从头开始并清除这个 Sass 文件。

让我们从编写一些嵌套的 CSS 开始:

.class-name{

background: #000;

p{

color: #fff;

span{

color: #eee;

}

}

}

如果您随后在命令行中使用compass compile对其进行编译,并从样式表的目录中打开编译后的 screen.css 文件,您会发现编译后的代码如下所示:

/* line 1, ../sass/screen.scss */

.class-name {

background: #000;

}

/* line 3, ../sass/screen.scss */

.class-name p {

color: #fff;

}

/* line 6, ../sass/screen.scss */

.class-name p span {

color: #eee;

}

您会注意到,除了生成的 CSS 之外,每个选择器都有一个注释,告诉您可以在 Sass 文件中找到该选择器的行号和文件。

正如你可能已经意识到的,每次做出改变都运行compass compile并不实际。这就是compass watch命令的用武之地。compass watch命令的作用是监视你的 Sass 文件,所以如果你改变了元素的背景颜色,它会自动重新编译。这样,任何进一步的更改都会自动编译,您现在可以运行以下命令:

compass watch

前面我讨论了如何将几个片段(CSS 文档的一部分)导入到一个文档中。在这里我将解释这是如何与 Sass 一起工作的。

首先,您将为片段创建一个目录,在这种情况下我们称之为partials。在这个文件夹中,你将创建两个名为_layout.scss_typography.scss的 Sass 文件。在这些分部之前加上下划线的原因是告诉编译器这是一个分部,不应该单独编译。

Inside _layout.scss 您将把这些基本样式:

.container{

max-width: 960px;

margin: 0 auto;

}

Inside _typography.scss 您将把这些基本样式:

html{

font-size: 100%;

}

h1{

font-size: 3rem;

}

p{

font-size: 1.2rem;

}

然后,您可以使用@import将它们包含在 screen.scss 文件中。screen.scss 代码中的代码将如下所示:

@import "partials/typography";

@import "partials/layout";

您会注意到,尽管原始文件以下划线为前缀,并带有文件扩展名的后缀,但在导入部分文件时,这些都是不必要的。因为您在命令行中运行了compass watch命令,Compass 正在监视这些文件的变化,所以在保存 screen.scss 文件时,该文件将被重新编译为 css。如果您接下来查看 screen.css 输出文件,您会看到这两个文件现在包含在同一个 css 文件中:

/* line 1, ../sass/partials/_typography.scss */

html {

font-size: 100%;

}

/* line 5, ../sass/partials/_typography.scss */

h1 {

font-size: 3rem;

}

/* line 9, ../sass/partials/_typography.scss */

p {

font-size: 1.2rem;

}

/* line 1, ../sass/partials/_layout.scss */

.container {

max-width: 960px;

margin: 0 auto;

}

导入的 Sass 文件按照声明的顺序进行编译和插入。请注意,注释强调了代码的每一部分来自哪个部分。

看了 Sass 的一些基本用法后,希望您对如何使用这些工具编译 CSS 有了很好的理解。我已经介绍了 CSS 预处理程序的核心功能,记住这些也适用于 Sass 是很重要的。

较少的

另一个很受欢迎的 CSS 预处理程序是 LESS。我之前介绍了 CSS 预处理器通常会包含的一些不同的特性,不幸的是 LESS 不支持编写自己的自定义函数,然而,讨论的所有其他特性都是受支持的。

Twitter 的开发者已经发布了几个使用较少的开源项目,最流行的是 Bootstrap 然而,他们也发布了一个名为 recession 的代码质量工具,这是一个测试 LESS 和 CSS 文件代码质量的工具。

装置

使用 LESS 有几种不同的方法,这里我将重点介绍使用命令行工具。LESS 命令行工具是用 Node.js 编写的,所以您需要使用npm来安装它。您将使用的命令是:

npm install -g less

使用

要开始使用 LESS,你需要建立一个文件夹,你可以调用less:

mkdir less

一旦创建了这个文件夹,您将使用文本编辑器创建一个名为 screen.less 的 LESS 文件。让我们从向 LESS 文件添加一些嵌套代码开始:

.class-name{

background: #000;

p{

color: #fff;

span{

color: #eee;

}

}

}

有了这些,您现在可以使用lessc命令行工具将 LESS 文件编译成 CSS。您需要提供要编译的文件名以及编译后的文件名:

lessc screen.less screen.css

文件编译完成后,您可以查看 screen.css 文件中的输出:

.class-name {

background: #000;

}

.class-name p {

color: #fff;

}

.class-name p span {

color: #eee;

}

输出与没有预处理器的情况下的编码非常相似,但是,实际编写的代码要简单得多。注意,与 Sass 不同,LESS 不会在源代码中添加调试注释。

我在前面讨论了导入的概念,并解释了如何在 Sass 中这样做,在 LESS 中非常相似。您只需使用一个@import语句来包含几个不同的文件。

您首先需要为这些片段创建一个目录,在本例中,您可以将其命名为partials。在这个文件夹中,你将创建两个名为_layout.less_typography.less的小文件。为了使 LESS 不编译部分代码,可以在文件前加一个下划线。

_layout.less里面,你会把这些基本样式:

.container{

max-width: 960px;

margin: 0 auto;

}

Inside _typography.less,你会把这些基本样式:

html{

font-size: 100%;

}

h1{

font-size: 3rem;

}

p{

font-size: 1.2rem;

}

然后您需要更新 screen.less 来使用@import语句导入这两个文件。与 Sass 不同,您需要提供 LESS 文件的完整路径,包括文件扩展名:

@import "partials/_typography.less";

@import "partials/_layout.less";

要将 LESS 文件重新编译成 CSS,只需重新运行前面的命令:

lessc screen.less screen.css

然后将screen.less文件编译成 screen.css 文件。如果您现在查看screen.css文件,您会看到这两个文件现在在同一个文档中,其顺序与您用@import语句声明它们的顺序相同:

html {

font-size: 100%;

}

h1 {

font-size: 3rem;

}

p {

font-size: 1.2rem;

}

.container {

max-width: 960px;

margin: 0 auto;

}

命令行并不是减少使用的唯一方式。当你开发的时候,你可以选择直接使用 LESS 文件作为链接,就像你在页面中包含 CSS 一样。除此之外,您还包括了less.js,这是一个将在浏览器中解析 LESS 文件的库。您将添加到 head 元素中的代码如下所示:

<link rel="stylesheet/less" type="text/css" href="screen.less" />

<script src="less.js" type="text/javascript"></script>

这使您能够在开发时快速测试代码,但是,建议您不要以这种方式部署设置较少的实时代码,因为浏览器需要在呈现样式之前解析文件。另外,添加less.js解析器当然也会增加页面的权重。

使用 CSS 预处理程序的问题

正如我前面提到的,使用 CSS 预处理器可以为您的开发带来巨大的好处,但是,它们也有缺点。

使用 CSS 预处理器时,您可能会遇到的第一个问题是,它会导致 CSS 文件变得臃肿。这是因为当你写代码的时候,很容易嵌套你的选择器,直到你的选择器变得非常具体和非常长。尽管这不是预处理器的错误,但由于不断嵌套的诱惑,这是一个容易陷入的陷阱。一个很好的例子可能是你想设计一个按钮标签的样式。如果您过度嵌套 CSS 预处理程序代码,它可能看起来像这样:

.main

.container{

.row{

.column{

p{

button{

color: #000

}

}

}

}

}

}

该嵌套代码将编译成如下所示的 CSS:

.main .container .row .column p button{

color: #000

}

在可能的情况下,您可以简单地将目标指向p中的一个按钮,因为在这种情况下,父节点可能与正确定位锚点无关:

p{

button{

color: #000

}

}

看到这个例子后,你应该意识到意外地开始嵌套是多么容易。这实际上是很多开发人员第一次使用 CSS 预处理程序时犯的一个非常常见的错误,所以如果您以前犯过这个错误,请不要担心。一个很好的规则是永远不要让你的嵌套超过三个选择器的深度,这意味着生成的 CSS 将会有更短的选择器。此外,通过不过度嵌套您的代码,可以更容易地使 CSS 可重用。

使用 CSS 预处理程序的另一个缺点是使用它们有一个轻微的学习曲线。尽管嵌套和变量等一些更简单的东西确实很容易掌握,但学习如何编写混合和函数可能需要更长的时间。尽管我在这里讨论的一些预处理程序之间存在语法差异,但是许多核心概念是相同的,所以一旦您选择了一个,就很容易转移到不同的预处理程序。

最后一个缺点是,在项目中设置使用 CSS 预处理器可能需要您对工作流进行更改。您不仅需要在每次保存时编译预处理器代码,还可能需要在部署过程中添加额外的步骤,以便在生产模式下编译预处理器代码。

选择预处理程序

我已经讨论了两个最流行的 CSS 预处理器 LESS 和 Sass,您已经看到了两者之间的一些差异。

Sass 本身提供了很多功能,对函数的支持是一个特别突出的特性。如果您将 Compass 添加到等式中,那么选择 Sass 作为您的预处理器将会增加很多价值。首先,Compass 包括一个 mixins 库,允许您生成不同版本的前缀代码。这比自己定义所有前缀要简单得多。此外,如果您提供一个装满图像的文件夹,Compass 将为您生成图像精灵和 CSS,并在您的页面上显示它们。

然而,LESS 也提供 mixin 库,试图通过提供输出前缀代码的 mixin 来提供类似的功能。然而,没有什么比 Compass 更好的了。

凉亭

Bower 是 Twitter 开发者的一个包管理器,它允许你安装前端包。使用包管理器的主要好处是,不用从 GitHub 或 packages 网站手动下载包,只需从命令行将其添加到项目中。此外,在下载软件包的同时,Bower 将下载它可能拥有的任何依赖项。一个简单的例子是,如果您要下载一个 jQuery 插件,它会检查您当前是否安装了 jQuery,如果发现没有安装 jQuery,它也会为您下载 jQuery。

除了允许你安装软件包之外,Bower 还允许你将软件包更新到新的版本,这意味着用最新版本的库来更新你的项目要容易得多。

装置

Bower 是一个节点包,因此需要安装节点才能工作。可以使用节点程序包管理器通过以下命令安装它:

npm install –g bower

使用

使用 Bower 最简单的方法是简单地运行bower install <package-name>,所以如果您想将 jQuery 安装到这个项目中,您可以使用:

bower install jquery

但是,有时您可能希望指定要安装的软件包的版本。最可能的原因是最新版本缺乏对您需要支持的浏览器的支持。jQuery 就是一个例子,它的 2.x 分支不能在 Internet Explorer 8 和更早的版本中工作,所以如果你想支持这些浏览器,你需要使用 1.x 分支。安装一个特定版本的包可以通过在包名后使用#version-number来实现。对于 jQuery,安装 1.10 版需要以下命令:

bower install jquery#1.10

使用 Bower 的好处之一是,您不需要在自己的源代码控制库中保存依赖项的副本。事实上,我们鼓励您在 bower.json 文件中简单地保存一个依赖项列表。为了能够以这种方式使用 Bower,首先需要初始化项目,这可以通过运行以下命令来实现:

bower init

这是启动一系列关于您的项目的问题,然后用这些信息生成一个 bower.json 文件。然后,您可以开始添加依赖项。有两种类型依赖关系:

General dependencies: These are the core dependencies used by your web site or web application in production. It is also assumed you will use these dependencies in production as well.   Development dependencies: These are the dependencies used specifically when you are developing your site, which might include unit testing, packaging scripts, and documentation generation.

如果您想将依赖项保存为开发依赖项,您可以简单地将—save-dev标志附加到命令的末尾:

bower install jquery#1.10 –save-dev

类似地,如果您想将依赖项保存为普通的依赖项,您可以简单地将--save标志附加到命令的末尾:

bower install jquery#1.10 --save

将依赖项保存到 bower.json 文件后,另一个想要建立项目的开发人员可以从存储库中下载项目,然后只需在项目的根目录中运行bower install即可自动下载所有需要的依赖项。

咕哝

Grunt ( http://gruntjs.com/ )是另一个使用 Node.js 构建的工具。它有时被称为构建工具,但是,这有点不准确,因为构建工具的唯一目的是将一些源代码转换成最终产品。相反,Grunt 是一个任务跑步者;关键的区别在于,虽然其中一个任务可能是构建您的代码,但它也可以用于运行您想要自动化的任何一系列任务,包括单元测试、回归测试和代码质量检查。对于一个大型项目,拥有一个任务运行器是非常宝贵的,因为它使您能够优化您的工作流程。

使用像 Grunt 这样的任务运行器有几个主要的好处,最明显的是,它可以自动完成你原本需要手动完成的任务,从而节省你的时间。另一个好处是它减少了人为错误的机会——我的意思是,一旦任务被设置,它们每次都以相同的方式运行。如果这是一个手动过程,出错的可能性会更大。

一个大型的开发人员社区已经围绕 Grunt 走到了一起,帮助构建一个令人惊叹的可插入任务生态系统,这些任务可以添加到您的 Grunt 构建中。这意味着有插件可以处理从构建 Sass 到在构建任务完成时通知您的所有事情。

装置

Grunt 接口分为两个组件:第一个是命令行接口组件,它是全局安装的。第二个组件是安装在您的项目上的本地 Grunt 安装。让我们从安装命令行接口组件开始,它是使用命令中的npm安装的:

npm install –g grunt-cli

使用

要开始使用 Grunt,您需要首先建立一个 package.json 文件(如果您还没有的话),只需运行以下命令:

npm init

运行此命令后,将会询问您一些有关项目的信息,这些是您将会被问到的问题:

Name: This is the name of your project, which should not contain spaces or any special characters.   Version: The current version of your project.   Description: The description of your project (optional).   Entry point: This is not applicable if you are just using Grunt on a project. It is used when you are writing a Node.js package (optional).   Test command: A command used to run tests on your project, for a Grunt project you can point this to a Grunt task (e.g., grunt test --verbose) (optional).   Git repository: The URL to the repository used for your project (optional).   Keywords: The keywords about your project.   Author: Your name.   License: If you are distributing the code, what license does it fall under.

一旦您回答了这些问题,您将看到 package.json 文件的代码。如果没问题,只需按回车键。该项目的 package.json 如下所示:

{

"name": "gruntproject",

"version": "1.0.0",

"description": "First Grunt Project",

"scripts": {

"test": "grunt test --verbose"

},

"author": "Jonathan Fielding",

"license": "MIT"

}

您现在需要在您的项目上执行本地 Grunt 安装,这是通过使用命令npm再次完成的:

npm install grunt --save-dev

这会下载 Grunt 及其依赖项,并将它安装在 node_modules 文件夹中。除此之外,通过使用--save-dev标志,您还可以将它添加到存储在 package.json 中的依赖列表中。因此,下一步是创建一个 Gruntfile。

Gruntfile 是决定您能够运行的任务的文件。要开始使用 Gruntfile,您需要首先创建 Gruntfile.js,并在其中设置基本配置,如下所示:

module.exports = function(grunt) {

//Grunt configuration goes here

grunt.initConfig({

pkg: grunt.file.readJSON('package.json')

});

};

Grunt 配置将加载您之前创建的 package.json 文件,并将其存储在 Grunt 的配置中,这意味着您可以访问有用的信息,如项目名称和当前版本号,而不必再次声明这些信息。准备好这个基本文件后,您现在可以找到一些对这个项目有用的简单任务。要查找插件,只需在 http://gruntjs.com/plugins 进行搜索。Grunt 网站的这个页面允许您搜索所有不同的可用 Grunt 插件。在这一章的前面,我讨论了 CSS 预处理器的使用,所以你可以寻找一个将 Sass 编译成 CSS 的任务。如果您在 Grunt 网站上搜索 compass,您可以找到一个名为 contrib-compass 的 Grunt 插件,这就是您将在下面的示例中使用的任务。

要安装 contrib-compass 插件,您需要再次使用npm,在插件名称前加上grunt-。您还需要使用--save-dev标志将其作为依赖项保存在 package.json 文件中。安装插件的完整命令是:

npm install grunt-contrib-compass --save-dev

下载完成后,您可以将 Grunt compass 插件的配置添加到 Grunt 文件中。您需要做的第一件事是提供配置选项,通过添加到传递给initConfig方法的对象中来完成。对于 Compass 插件,您需要添加一个名为compass的属性,在这个属性中,您可以定义不同的目标,并为每个目标包含不同的选项。对于这个例子,您将简单地建立一个名为dev的开发目标,并提供一些简单的选项来告诉 Compass 文件的位置以及您正在使用的开发环境:

grunt.initConfig({

pkg: grunt.file.readJSON('package.json'),

compass: {

dev: {

options: {

sassDir: 'sass',

cssDir: 'css',

imagesDir: 'images',

environment: 'development',

httpGeneratedImagesPath: ' images'

}

}

}

});

准备就绪后,您现在需要设置任务,您将通过加载您希望能够使用的任务来完成。在这种情况下,您希望能够使用 Compass 任务。这是使用grunt.loadNpmTask方法加载的。您将这些命令放在配置选项之后,因此对于 Compass 插件,命令应该是:

grunt.loadNpmTasks('grunt-contrib-compass');

在测试之前,您需要编译一些文件。让我们使用您在前面学习 CSS 预处理程序时使用的 Sass 文件。准备就绪后,您可以返回到终端并运行命令grunt compass,它将把 Sass 编译成 CSS。如果你现在检查 CSS 文件夹,你会发现 Sass 已经按照你的预期编译好了。

Grunt 的强大之处不仅仅在于你可以单独运行单个任务,而是在于定义任务,这些任务本身运行一系列不同的任务。为此,您可以使用方法grunt.registerTask,该方法允许您定义一个任务名称以及将由该任务运行的一组任务。对于这个例子,数组中只有一个任务,它是指向您定义的dev目标的 Compass 任务:

grunt.registerTask('default', ['compass:dev']);

在上面的例子中,您为任务使用了名称default,这很特别,因为它不像其他任务那样作为参数在命令行中传递,默认任务只是在您运行grunt命令时运行。

到目前为止,我一直专注于使用 Grunt 来编译 Sass 代码,虽然您以前可以简单地通过使用compass命令行工具来完成这项工作,但是通过使用 Grunt,您可以让它更多地成为您工作流程的一部分。已经有超过 3000 种不同的 Grunt 插件可用,其中一些最流行的包括:

grunt-contrib-watch: Allows you to watch files for changes and run tasks based on what has changed.   grunt-contrib-uglify: Allows you to minify and concatenate your JavaScript files.   grunt-contrib-copy: Copies files and folders.   grunt-contrib-jshint: Checks your JavaScript for common errors and mistakes.

你应该从中吸取的是,Grunt 是一个非常强大和灵活的工具,虽然最初设置它可能会令人望而生畏,但一旦你设置好了任务,它将为你节省大量的时间。有一个巨大的社区正在开发插件和扩展 Grunt,所以随着时间的推移,它的有用性将会继续增长。

吞咽

Gulp ( http://gulpjs.com/ )是一个类似 Grunt 的工具,它是一个任务运行器,可以用来运行一系列任务。任务本身是通过向 Gulp 添加插件来添加的。与 Grunt 采用的基于配置的方法不同,Gulp 采用的方法是您编写代码来完成您的任务。它这样做的方式是,它可以获取一些文件,然后您可以通过需要运行的不同任务来处理这些文件。一个例子是一系列 JavaScript 文件,您可能首先想要连接这些文件,然后将其缩小,要实现这一点,您只需在将最终文件保存回磁盘之前,通过一个“concat”任务,后跟一个“uglify”。这与 Grunt 的工作方式非常不同。

Gulp 采用的这种基于流的方法非常快,这在詹德·马蒂诺( http://labs.tmw.co.uk/2014/01/speedtesting-gulp-and-grunt/ )的测试中得到了证明,他发现在编译 Sass 时,Gulp 比 Grunt 快一秒多。Zander Martineau 还表示,预计随着时间的推移,用于 Gulp 的 Sass 插件将进一步优化,从而使编译速度更快。

安装 Gulp 是一个两步过程,第一步是在您的系统上全局安装 Gulp,这是通过以下命令实现的:

npm install –g gulp

下一步是将 Gulp 安装到您的项目中。为此,您需要使用命令npm:

npm install --save-dev gulp

然后您需要创建一个名为 gulpfile.js 的文件,它是决定您想要运行的任务的脚本文件。在其中,您将首先设置基本代码:

var gulp = require('gulp');

然后,您需要开始添加插件。为了比较 Gulp 和 Grunt 的工作原理,让我们用同样的例子来编译 Sass。为此,您需要使用npm将 gulp-sass 插件安装到项目中:

npm install gulp-sass

然后需要将 gulp-sass 插件放入 gulpfile.js 文件。因为 gulpfile.js 是在 Node 中编写的,所以只需使用require将模块加载到一个变量中:

var sass = require('gulp-sass');

下一步是为 Sass 添加任务,以便它能够将 Sass 文件编译成 CSS。通过使用gulp.task方法,将任务的名称作为第一个参数传递,将函数作为第二个参数传递,可以做到这一点。与 Grunt 类似,如果您将名称设置为default,那么当您在命令行中简单地运行gulp命令时,这个任务就会运行:

gulp.task('default', function() {

});

设置好该任务后,您现在可以开始向该任务添加功能。首先,您需要使用gulp.src方法获取您想要处理的源文件,在 Sass 的情况下,这是您想要编译的原始scss文件。您可以使用pipe方法将文件传输到 S ass方法,它会告诉 Gulp 您想要编译这些文件。最后,您可以使用pipe将编译后的输出传递给gulp.dest,?? 将输出编译后的源文件:

gulp.task('default', function() {

gulp.src('./scss/*.scss')

.pipe(sass())

.pipe(gulp.dest('./css/'));

});

这个例子突出了 Gulp 的关键特性,即如何通过管道将文件从一个方法传递到另一个方法,并在传递过程中对它们执行任务。gulp 插件的一个关键原则是,每个插件处理一件事情,通过组合这些插件,您可以灵活地构建完全符合您需求的任务。

有各种不同的插件可以与 Gulp 一起使用。一些受欢迎的是:

gulp-uglify: The Uglify plug-in can be used to minify JavaScript files.   gulp-concat: The Concat plug-in allows you to concatenate multiple files into a single file.   gulp-jshint: The JSHint plug-in allows you to lint your JavaScript files for common errors.

Gulp 的主要弱点是,在撰写本文时,它没有 Grunt 那么成熟,这意味着它没有那么多插件,社区仍在学习如何最好地使用它。这并不意味着你不应该使用 Gulp 围绕它有一个快速发展的社区,你只需要确保你的项目需要的插件是可用的。

脚手架

开始一个新项目时,整合初始模板可能非常耗时,因为它通常包括:

Putting together the base HTML   Pulling together any JavaScript libraries you want to use   Adding any CSS frameworks or CSS grids you want to use.

幸运的是,已经发布了许多不同的工具,允许您生成这些起始模板。让我们来看看其中的一些工具,看看如何将它们集成到您的工作流程中。

自耕农

约曼( www.yeoman.io )是一个项目搭建工具,允许你快速搭建一个基于预定义生成器的项目。有各种各样的生成器可用,尽管如果没有一个符合您的需要,您甚至可以自己编写一个。

一些可用的发电机包括:

webapp   angular   ember   backbone   chromeapp   chrome-extension   bootstrap

当使用这些生成器时,Yeoman 将搭建文件夹结构和一些特定于生成器的文件。生成器还会在 bower.json 文件中设置任何依赖项,然后运行 Bower install 来下载所有需要的依赖项。

装置

要开始使用 Yeoman,您需要使用npm安装节点包。Yeoman 的节点包叫做yo,它需要在全球范围内安装,这样你就可以在命令行的任何地方访问它来启动你的项目。要在全球范围内安装 Yeoman,您需要使用–g旗帜。因此,安装 Yeoman 的完整命令是:

npm install -g yo

安装 Yeoman 可能需要相当长的时间,因为它需要安装许多依赖项。一旦安装了 Yeoman,只需使用以下命令就可以测试它是否安装正确:

yo

使用

要使用 Yeoman 搭建项目,您需要为您的项目创建一个目录。这可以通过 Finder/Windows 资源管理器或直接从命令行完成。对于此示例,让我们直接在命令行中创建文件夹目录:

mkdir project_directory

cd project_directory

一旦进入目录,就可以开始使用 Yeoman 来搭建项目了。第一次安装时,约曼没有配备任何发电机,因此,要开始安装,你需要安装一台发电机。您可能使用的最常见的生成器是 webapp 生成器,所以让我们首先安装它。第一步是运行yo命令。

然后,您需要使用箭头键选择“安装发电机”并按 Enter 键。然后,您将能够通过输入术语 webapp 来搜索 webapp 生成器(参见图 7-2

A978-1-4302-6695-2_7_Fig2_HTML.jpg

图 7-2。

Yeoman shows a list of generators it finds

然后,您需要使用箭头键向下滚动到“generator-webapp”,选择它后,您只需按 Enter 键。Yeoman 现在将为您下载并安装 webapp 生成器。

您现在应该会看到一个名为“运行 Webapp 生成器”的附加命令在括号中,它将给出您当前安装的发电机的版本。如果列表没有更新,只需选择“让我离开这里”并再次运行yo命令。要运行生成器,只需选择它并按回车键。

有些生成器允许您在设置模板之前选择选项,从而额外配置正在设置的脚手架。webapp 就是这样一个生成器,它将为您提供一系列可配置的选项(参见图 7-3 )。

A978-1-4302-6695-2_7_Fig3_HTML.jpg

图 7-3。

Yeoman gives a list of configurable options

要选择/取消选择这些选项,只需使用箭头键上下移动,并使用空格键来选择/取消选择。对于选定的选项,圆圈填充为绿色,对于未选定的选项,圆圈为空。

当您现在按下回车键时,Yeoman 将首先搭建您的项目,包括为 Grunt 和 Bower 准备配置文件,然后运行npm install 和 Bower install 命令,这将下载所有需要的依赖项。这部分过程可能需要一段时间,取决于您的互联网连接速度。

然后,约曼将确认你的项目已经全部设置好了。

如果在任何时候您发现事情不像您预期的那样工作,您的安装可能有问题。为了对此进行诊断,Yeoman 有一个名为yo doctor的命令,它将检查一切是否按预期工作。只需运行这个命令,Yeoman 就会告诉您是否有任何需要解决的问题。

在这一阶段,你将有一个完全脚手架项目,所以你可以继续建设网站。然而,需要注意的是,许多生成器都会附带预定义的繁重任务。因此,让我们来看看用 webapp generator 设置的简单任务:

grunt: Runs jshint on the JavaScript following by the test and build commands.   grunt build: Compiles the projects, including minifying/concatting js, building the Sass into minified CSS, and compressing and renaming images (for cache busting).   grunt watch: Watches the JavaScript, CoffeeScript, Sass, and style sheets to detect any changes and recompiles if they change.   grunt server: Creates a Node server for running the project, watches for any file changes, and refreshes browser on file change.   grunt test: Runs any tests set up for the project.

您现在已经看到了使用 Yeoman 搭建项目是多么容易。它是快速开始项目的一个很好的工具,并且有各种各样的生成器可用,很容易找到一个适合你的。

如果您想进一步使用 Yeoman,您可以选择编写您自己的生成器,该生成器专门针对您希望如何搭建您的站点。一旦你编写了自己的生成器,你就可以一次又一次地使用它,并在使用过程中不断完善它。当然,你可以开源,这样其他人也可以使用它。

咕哝初始化

替代的命令行工具 grunt-init(gruntjs.com/project-scaffolding)使您能够快速搭建您的项目。

装置

要开始使用 grunt-init,请运行命令行:

npm install -g grunt-init

使用

尽管 grunt-init 与 Yeoman 有很多相似之处,但是安装模板的方式却非常不同。使用 grunt-init,您需要将生成器添加到一个名为。用户主目录中的 grunt-init。

将它们安装到正确目录的一个简单方法是使用 Git 将它们克隆到。grunt-初始化文件夹。Pascal Duez 开发的 web app grunt-init web app 模板可以在 https://github.com/pascalduez/grunt-init-webapp 找到。您可以使用以下命令行通过克隆来安装此模板:

git clonehttps://github.com/pascalduez/grunt-init-webapp.git

一旦模板存储库的 Git 克隆完成,您就可以使用 webapp 模板来搭建新项目。

要开始,您需要创建项目目录,这可以通过 Finder/Windows Explorer 或直接从命令行完成。对于此示例,让我们直接在命令行中创建文件夹目录:

mkdir project_directory

cd project_directory

现在,您可以使用 grunt-init 为 web 应用程序生成脚手架,这可以通过运行命令并向其传递您之前下载的模板的名称来实现:

grunt-init webapp

运行此命令后,将会询问您以下问题:

Please answer the following:

[?] WebApp package and root directory. (project_directory)

[?] WebApp name. (A human-readable name for the app: Project Directory)

[?] WebApp description. (A human-readable description of the app.)

[?] Version (0.1.0)

[?] Project git repository (git://github.com/jonathan/project_directory.git)

[?] Project homepage (https://github.com/jonathan/project_directory

[?] Author name (Jonathan Fielding)

[?] Author url (none)

[?] Licenses (MIT)

[?] Do you need to make any changes to the above before continuing? (y/N)

回答完这些问题后,grunt-init 将下载并安装脚手架所需的组件。

与 Yeoman 类似,grunt-init 将预配置 grunt file—在 webapp 模板的情况下,它将具有以下命令:

grunt: Runs jshint on the JavaScript.   grunt server: Creates a Node server for running the project, watches for any file changes, and refreshes the browser on file change.

您现在已经看到了使用 grunt-init 是多么容易。Grunt-init 使安装新模板变得非常简单,因为您可以简单地将它们放入{/中。grunt-init/目录。在本例中,您从公共 Git 存储库中克隆了一个模板,但是,您可以在私有存储库中维护自己的模板,这样您就可以与朋友或同事共享它们。或者,您可以简单地编写自己的生成器并将其放在目录中,不需要源代码控制。

初始化

Initializr ( http://www.initializr.com/ )与 Yeoman 和 grunt-init 非常不同,它是一个基于 web 的工具,可以生成一个可以下载为 zip 存档的脚手架。

使用

要开始使用 Initializr,只需访问 Initializr 网站,之后您将看到该模板的三个起点选项(见图 7-4 )。

A978-1-4302-6695-2_7_Fig4_HTML.jpg

图 7-4。

Initializr web site allows you to choose what type of site you want

选择一个选项后,您将看到配置模板的更多选项。一旦你对你的选择感到满意,你可以简单地点击下载它!按钮(见图 7-5 )。

A978-1-4302-6695-2_7_Fig5_HTML.jpg

图 7-5。

Initializr provides you with the option to fine-tune your template

一旦你下载了搭建好的模板,你就可以马上开始编码了。

如您所见,Initializr 对于快速获得项目模板非常有用,但是,它也有其局限性。关键的限制是所提供的 CSS 只是普通的 CSS,没有提供预处理程序的设置,所以你必须自己做。如果你不打算在你的项目中使用 CSS 预处理器,那么你应该没问题,但是意识到这一点很重要。

脚手架工具概述

我已经讨论了搭建项目的三种不同的工具,其中两种是命令行工具,一种是基于 web 的工具。

首先,Initializr 是一个非常好的工具,可以非常快速地生成脚手架,但是它在创建定制模板的能力上有所欠缺,这一点在易用性上得到了弥补。如果你想要一个快捷、易用的模板,你绝对应该考虑使用 Initializr 然而,如果你需要一个更具体的模板,那么你需要看看约曼或格朗特。

如果你打算主要使用你自己的模板而不是下载预置模板,那么 grunt-init 可能是你唯一的选择。然而,如果你希望能够搜索大量不同的模板,那么 Yeoman 将为你提供额外的灵活性。

其他有用的工具

杰林特

jslint 工具使您能够从 JavaScript 代码中检测错误和潜在问题。包括 Mozilla、脸书和 Twitter 在内的世界各地的开发人员都依赖它来确保它检测到的问题不在它的代码库中。

jshint 能够检测的问题类型是语法错误、泄漏变量和与隐式类型转换相关的错误,它确保您的代码遵循定义的编码约定。

使用 jshint 的主要好处是:

Shows you any simple errors or mistakes in your JavaScript code   Enables you to enforce your team’s coding conventions   Fully configurable so you are not forced to follow someone else’s standards

我之前讨论过在 Grunt 中使用 jshint,但是,您也可以很容易地将它作为一个独立的命令行工具来快速检查代码的质量。

装置

jshint 工具是一个命令行工具,可由节点包管理器安装。要安装 jshint,只需运行以下命令:

npm install jshint -g

使用

使用 jshint 非常简单——您只需将您想要 lint 的 JavaScript 文件的路径传递给jshint命令:

jshint jsfile.js

然后,jshint 将在您的 JavaScript 文件中查找任何问题,这些问题将与导致问题的行号和字符位置一起输出(参见图 7-6 )。

A978-1-4302-6695-2_7_Fig6_HTML.jpg

图 7-6。

Any issues found by jshint are output to the command line

服务

服务器 serve 是一个简单的节点服务器,它将为您在终端中使用的当前文件夹提供服务。这意味着,如果您需要在本地机器上的服务器上快速抛出一些东西,您可以使用 serve,然后在浏览器中导航到 localhost:3000 来查看您的站点。

装置

要安装服务器,请运行以下命令:

npm install serve -g

使用

开始使用 serve 的最简单方法是导航到您希望为您的浏览器提供服务的目录,然后简单地运行命令:

serve

这将为 localhost:3000 提供当前目录,此外,您还可以指定其他选项:

-F, --format <fmt>: Specify the log format string   -p, --port <port>: Specify the port [3000]   -H, --hidden: Enable hidden file serving   -S, --no-stylus: Disable stylus rendering   -J, --no-jade: Disable Jade rendering   --no-less: Disable less CSS rendering   -I, --no-icons: Disable icons   -L, --no-logs: Disable request logging   -D, --no-dirs: Disable directory serving   -f, --favicon <path>: Serve the given favicon   -C, --cors: Allows cross origin access serving   –compress: Zip or deflate the response

这些选项使用起来非常简单。如何更改端口的一个示例是使用以下命令:

serve --port 2000

-是啊

关于苹果 iOS 模拟器的一个主要抱怨是打开它需要多少努力。首先,你必须打开 Xcode,然后进入菜单栏,从开发者工具中选择模拟器。幸运的是,有人开发了一个小工具,可以让我们快速、轻松地从命令行打开 iOS 模拟器,这个工具叫做 ios-sim。

装置

ios-sim 是使用npm安装的,要安装它,您需要运行:

npm install ios-sim -g

使用

要使用 ios-sim 命令行工具启动 iOS Simulator,只需运行:

ios-sim start

然而,您可以通过传递--family参数来选择您想要使用的设备:

ios-sim start --family ipad

如果你想加载 retina iPhone,你可以在命令后面加上--retina:

ios-sim start --retina

混合

Mixture ( http://mixture.io/ )是一个将搭建、预处理、测试、模板化、构建和部署整合到一个界面中的工具。

当您第一次打开 Mixture 时,会出现一个窗口,您可以在其中选择想要用来搭建项目的样板文件(参见图 7-7 )。

A978-1-4302-6695-2_7_Fig7_HTML.jpg

图 7-7。

Mixture allows you to select from a variety of popular templates

你可以通过点击你想要的来选择一个样板文件,然后你会被要求提供一个项目的目录。选择目录后,Mixture 将从 GitHub 下载样板文件所需的文件。

设置完成后,您可以点击本地查看(见图 7-8 )直接在浏览器中查看您的项目。

A978-1-4302-6695-2_7_Fig8_HTML.jpg

图 7-8。

A project shown in Mixture

Mixture 会监视你的文件,如果你做了任何修改,它会从 CSS 预处理器处理 CSS 的编译,并自动为你重新加载页面。这意味着您可以并排使用您的文本编辑器,并在进行更改时快速查看所做的更改。

Mixture 是一个非常强大的工具,虽然它缺乏使用 Yeoman、Grunt 和 Bower 的灵活性,但它使用起来要简单得多,因为只需点击几下鼠标,您就可以配置好一个项目并开始工作。当您的项目完成后,您可以轻松地将它部署到一个活动服务器上。

工作流程

到目前为止,我已经讨论了一些很棒的工具,然而,当你建立一个网站时,它们本身只是解决方案的一部分。当你开始将它们整合到一个工作流程中时,它们就会变得非常强大,帮助你更高效地构建网站。

当你开始看开发工作流程时,你可以把它分成五个独立的步骤,如图 7-9 所示。

A978-1-4302-6695-2_7_Fig9_HTML.jpg

图 7-9。

The five steps of the development workflow

更详细地了解这些步骤将有助于您准确理解每个步骤所涉及的内容。

脚手架

当您第一次启动一个新项目时,您通常会花时间设置项目目录和基本文件,并下载标准库,如 jQuery 和 respond.js。这个过程称为搭建,通常是开发过程的第一步。

在本章的前面,我讨论了三种可以用来搭建项目的工具:Initializr、Yeoman 和 grunt-init。这些工具本质上允许您通过自动化更快地完成构建的初始步骤,因此您只需告诉他们您需要什么。

抽象

在开发时,抽象是一种编写代码的方式,这些代码最终会被编译成另一种形式。在这一章中,我讨论了 CSS 预处理程序,它是你以前编写的 CSS 的抽象。然而,CSS 预处理程序并不是您可能在项目中使用的唯一抽象工具。有针对 JavaScript 的抽象工具,包括 CoffeeScript、Dart 和 Typekit,也有针对 HTML 的抽象工具,包括 HAML、Markdown 和 Emmet。

所有这些抽象的目的是让你的开发更有效率。他们通过多种方式做到这一点,包括添加功能,有时甚至简化原始形式。这是工作流的一部分,因为你是在抽象中开发,而不是在原始形式中。

循环

当你建立一个站点时,你通过定期在文本编辑器和浏览器之间切换来迭代,检查你所做的改变。这通常包括切换到浏览器、刷新、检查更改、返回到文本编辑器、进行更改,然后重复。此外,如果您正在使用某种抽象,您需要构建抽象,以便它可以在浏览器中显示。

这是迭代工具让我们的生活变得更容易的地方;它们是允许我们自动构建抽象的工具,当我们做出改变时自动刷新我们的浏览器,等等。

在这一章中,我介绍了几个不同的迭代工具,第一个是compass watch命令,每当 Sass 文件改变时,它将重建 CSS。这真的很重要,因为你不希望每次刷新浏览器页面时都需要手动重建。然而,这是一个单一目的的迭代工具,当你迭代你的项目时,你可能经常使用多个抽象或者想要更多的自动化。这样做的问题是,您不希望必须运行多个单一目的的工具,因为这不仅难以管理,而且还可能存在工具试图同时使用相同文件的潜在冲突。

我之前讨论的另一个工具是 task runner Grunt,正如您所了解的,它非常适合运行多个任务,因此将其用作迭代工具是有意义的,因为在每次迭代中,您都可以使用它来运行一系列任务。grunt 本身并不监视文件,但是,您可以使用 grunt-contib-watch 插件来添加这一功能,您可以像使用 Grunt 插件一样使用和配置该插件。不过,这意味着你可以让 Grunt 监视一个目录,当你对文件进行修改时,你可以让它自动运行你定义的一系列任务。这意味着您可以在单个工具中编译多个抽象,此外还有一些插件,允许您运行诸如重新加载浏览器和运行单元测试之类的任务,以检查您没有破坏任何东西。

除了 Grunt 之外,我还讨论了 Mixture,它是一个 GUI 工具,允许您为站点设置迭代工作流。Mixture 允许你编译一个抽象列表,它可以缩小你的 CSS/JS,这样当你做了一个文件改变时,两者都会为你重新加载浏览器。该工具的缺点是您不能添加任何自定义任务,因此,如果您发现有一个您想要的功能是该应用程序不支持的,您就不能添加它。

构建的迭代阶段是您花费大量时间的地方,因此在这里获得合适的工具非常重要。有许多工具可以帮助您改进处理迭代的方式,投入时间正确设置这些工具可以在以后节省您更多的时间。需要记住的一点是,您的迭代工作流可以按照您认为合适的方式发展,因此,如果您发现某些部分并不真正适合您的项目,您可以调整它以更好地满足您的需求。

建设

在您构建了您的站点之后,您将希望能够为实时部署构建您的代码。这可能包括运行单元测试、构建抽象、缩小 CSS 和 JS 以及压缩图像。

手动执行这些构建任务可能很耗时,因此这就是设置构建工具的重要之处。正如我前面所解释的,您可以使用 Grunt 来定义本身执行多个任务的任务,这意味着您可以为 Grunt 定义一个构建任务,它将运行您已经定义的一系列任务。当我讨论使用 Grunt 编译 Sass 时,我提到了如何为任务定义目标,以及为每个任务定义不同的选项。在编写构建任务时,您应该考虑在您的任务中使用这些目标,以允许您告诉 Compass 等任务您正在部署到生产中。

除了使用 Grunt 来建立你的网站,你也可以使用 Mixture。然而,类似于您如何在迭代阶段使用它的限制,您不能容易地在它们已经支持的基础上向构建过程添加额外的功能,这意味着您受限于 Mixture 支持的构建功能。

了解了如何优化您的构建工作流之后,很容易看到自动化构建过程是多么简单。因为您可能会一次又一次地使用这个构建过程,所以正确地使用它是很重要的。因此,确保您能够在这上面花费足够的时间,并且类似于您的迭代工作流,不要害怕对您的过程进行更改,并继续发展它以适应您的需求。

部署

站点开发的最后一步是将其部署到生产环境中;从历史上看,这可能是一个非常手动的过程,尤其是在只能通过 FTP 访问的服务器上。

您的部署方式很可能因您的服务器设置而异,因此您的工具需要允许您根据您的设置对其进行定制,而不是改变您的工作方式来适应工具。这就是为什么像 Grunt 这样的任务运行器对您的部署工作流是一个很好的补充,因为您可以定义自己的部署任务,该任务将执行您的特定设置所要求的步骤来部署站点。Grunt deploy 任务可以从可插拔任务中构建,这意味着构建适合您的工作流非常容易。

另一个能够自动化部署过程的工具是 Mixture。Mixture 能够将站点部署到两个 FTP 服务器上,对于开源站点,它能够部署到 GitHub 页面上。这意味着一旦配置完毕,您只需点击一个按钮就可以快速部署构建好的站点。主要问题是 Mixture 仅限于部署到 Mixture.io、FTP 服务器和 GitHub 页面,所以如果你的部署过程需要涉及到其他任何东西,那你就倒霉了。

工作流摘要

了解了开发人员工作流程中涉及的不同步骤后,您应该看看本章中讨论的每个工具是如何适应这些步骤的(参见图 7-10 )。

A978-1-4302-6695-2_7_Fig10_HTML.jpg

图 7-10。

How tools fit into the different parts of your workflow

值得注意的是,一些工具本身包含一个工作流,例如 Mixture。如您所见,Mixture 将直接将您从搭建阶段带到部署阶段。这意味着,如果您愿意,您可以在整个项目工作流程中使用单一工具(除了代码编辑器之外)。这样做的局限性在于,你没有足够的灵活性来按照你想要的方式工作。

或者,像 Grunt 这样的工具可以用来帮助你管理工作流程的重要部分;它可以将其他工具作为任务包含在内,为您提供一个通用的界面,供您在迭代代码、构建和最终部署时使用。以这种方式使用 Grunt 确实需要时间来正确设置,所以你需要花时间来充分利用 Grunt。一些工具只是单一用途的工具,它们都服务于特定的目的,但是对于大多数这些工具,有一个简单的插件来包装功能,并使其作为简单的任务可用。这使得它们更易于使用,并允许您将它们更好地集成到您的工作流程中。

一般来说,你需要确保你的工作流程是最适合你的。本章试图介绍各种可以用来改进工作流程的工具,但它们的帮助程度取决于您的工作方式。我建议您查看每种工具并尝试一下——有些您可能喜欢,有些您可能讨厌,但最重要的是了解可能对您有益的工具。

摘要

我们使用的工具可以对我们的开发工作流程产生巨大的影响,因为它们可以使我们以更高效的方式工作,允许我们在完成时交付一个更好的网站。

在本章的前面,我解释了如何使用命令行。理解如何使用命令行非常重要,所以我解释了一些基本的命令来帮助您入门。了解了命令行之后,我接着探索了如何使用版本控制来管理 web 站点的源代码。虽然这在团队中工作时特别重要,但在处理个人项目时也很有用,因为它允许您查看随着时间的推移您做了哪些更改,如果引入了 bug,它将允许您查看引入它的更改。

当设计你的网站时,也有一些工具可以让你的生活变得更简单。我解释了 CSS 预处理程序,它允许您使用 CSS 的抽象来编写样式,然后将抽象编译成 CSS,从而允许您以更快、更灵活的方式编写样式。

管理、安装和升级站点中的依赖项可能是一个乏味的过程,尽管已经讨论了 Bower 的使用,您可以看到有更简单的方法来实现这些目标。我还探讨了流行的任务运行器的优点和缺点,所以现在您应该知道在处理典型的繁重任务时如何利用它们。

最终,你的工作流程真的很重要,它可以是一件非常个性化的事情,所以选择适合你的工作流程很重要。然而,不要害怕随着时间的推移而改进你的工作流程,因为新的工具总是在开发,你的需求可能会随着时间的推移而改变。

在下一章,我们将看看如何通过管理用户旅程来优化我们的响应网站的用户体验。

八、让用户旅程响应迅速

用户的旅程是通过网站的一系列步骤,因此它是使用体验的一个重要因素。用户的旅程可能因网站而异,这取决于谁是目标受众,他们需要从网站获得什么,以及他们将如何使用网站。

有两个主要因素促成了一个网站的用户之旅:企业的需求和用户的需求。当涉及到业务需求时,这是站点的业务目标被确定的地方;这些可能是为了推动销售,增加参与度,或发展品牌知名度。当谈到用户的需求时,这就是用户在访问网站时试图达到的目标,包括他们的目标和动机。

考虑到这一点,让我们来看一个以健康俱乐部网站为特色的用户旅程示例。在这种情况下,用户需要找到有关健身俱乐部的信息,以便确定他们是否有兴趣注册。对于企业来说,他们的需求是销售健身俱乐部会员资格。因此,用户之旅应该通过研究如何将它们结合起来来满足这两种需求。这是通过清晰的用户旅程来实现的,首先为用户提供他们需要的关于健身俱乐部的信息,但最终通过将他们带到注册页面来鼓励他们注册。

虽然这里的例子并不复杂,但并不是所有用户的旅程都这么简单,或者在所有设备上都适用。如果旅程在您支持的一个或多个设备上没有意义,您应该考虑如何调整旅程,为用户提供更好的体验。

这一章将教你如何调整用户通过你的网站的旅程,以优化他们的用户体验,不管他们使用的是什么设备。为此,我将介绍:

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 中对此进行了说明。

A978-1-4302-6695-2_8_Fig1_HTML.jpg

图 8-1。

The journey the user takes on larger viewports

如果您了解这在较小的视窗中是如何工作的,那么您可以调整用户在站点中的行程,以便在点击图像时,它会打开一个新页面来显示较大的图像。此外,在新页面上,您需要确保包含返回到原始图库的链接。图 8-2 显示了用户浏览网站的步骤。

A978-1-4302-6695-2_8_Fig2_HTML.jpg

图 8-2。

The journey the user takes on smaller devices

实现这一点的方法是在图像周围设置一个链接到新页面的锚链接,对于较大的视窗,只需使用 JavaScript 在模式窗口中加载图像。我将在第九章中解释如何实现这一点。

产品搜索

通常情况下,如果你的网站是一个产品,你的用户很容易快速找到你的产品信息;然而,对于销售各种不同产品的大型零售网站来说,用户能够快速方便地找到他们正在寻找的产品和相关信息是非常重要的。

用户在大型零售网站上搜索产品的典型过程是在搜索框中输入产品名称,然后进入搜索结果页面,如图 8-3 所示。

A978-1-4302-6695-2_8_Fig3_HTML.jpg

图 8-3。

Path to the results page after entering a search term

这个旅程在一个响应式网站上工作得很好,但是,它没有考虑用户的情况,在这种情况下,结合了用户的当前位置和实际搜索的目的。

传统的情况下,用户可能会在家里的台式电脑上搜索产品,比较功能和价格。这种传统情况是过去大多数用户使用网站的方式。然而,随着智能手机的普及,出现了新的情况,有人可能在外面购物,看到他们感兴趣的产品,并希望确保他们得到最好的价格。为了做到这一点,用户拿出他们的手机,在网上搜索产品,也许是通过访问你的网站,搜索产品。

这种情境行为在以前是不会被考虑,但是对于一个响应式站点,开发人员现在需要开始假设一个站点可以在任何地方使用,因此在设计网站时需要考虑这一点。部分问题是访问者的情境行为不容易被检测到。开发人员不知道用户是在商店里还是在家里,虽然您可以尝试使用地理定位 API 来确定他们的位置,但这是不切实际的,原因有几个。您必须知道您的客户可能在的所有不同商店的位置,并且您还需要请求用户允许您使用他们当前的位置。

因此,与其试图检测用户的情况,不如对预期的用户情况进行有根据的猜测。要做到这一点,您应该研究用户选择使用特定设备的典型情况,然后考虑在这种特定情况下他们会从哪些功能中受益最多。此外,如果你已经有了一个网站的在线版本,并且你可以访问分析数据,你可以看看你的用户已经在使用你的网站做什么,以告知你如何针对某种情况优化你的网站。

如果您再次查看产品搜索的示例,您可能会认为用户在商店时很可能会使用该站点来查找价格。很自然,您会希望确保他们能够以尽可能少的步骤完成这项工作。这样做的第一步是优先考虑用于产品发现的内容;对于销售各种产品的网站,这可以通过确保当页面加载时,产品搜索框立即对用户可见来实现。如果你的网站专注于销售一些你自己的产品,你应该展示一些基本信息,包括价格,这样用户甚至不用离开他们登陆的页面就能找到他们想要的信息。

在较大的视窗中,虽然搜索仍然是网站的一大特色,但用户更可能只是浏览网站,并不完全确定他们在寻找什么(可能是为朋友寻找礼物,但不确定买什么)。因此,你要确保产品发现是网站的一个重要部分,根据以前的购买显示类别和建议。

除了如何确定内容的优先级,您还应该考虑如何使用渐进式增强来利用设备提供的新功能。产品搜索的一个例子是允许用户拍摄产品或条形码的照片来搜索产品。对用户的好处是,他们不需要输入复杂的产品名称,比如“Samsung ue40 f 5000 40”LED TV,而是简单地拍一张照片。

为了实现这一点,您需要确定设备是否支持摄像机 API,然后对于那些支持的设备,为用户提供一个额外的按钮,他们可以激活该按钮来加载摄像机。如果只找到一种产品,网站可以直接把用户带到该产品,否则他们将被带到一个搜索结果页面。本例的用户旅程如图 8-4 所示。

A978-1-4302-6695-2_8_Fig4_HTML.jpg

图 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 显示了用来查看我的网站的浏览器和操作系统的分解视图。

A978-1-4302-6695-2_8_Fig5_HTML.jpg

图 8-5。

Breakdown of different browsers being used to access my site in Google Analytics

知道哪些浏览器被用来访问你的网站是非常重要的,因为你想知道哪些浏览器你需要支持和测试你的网站。但是,因为您正在开发一个响应站点,所以了解哪些设备正被用来访问站点也很重要,因为这将使您能够确保您的站点在访问站点时最常用的设备上进行测试。

谷歌分析的设备信息有两个部分;首先,是概述页面。要访问 Google Analytics 的浏览器部分,您可以单击受众菜单项,然后单击移动设备,然后单击概览。在本节中,您将看到按设备类别分类的设备,目前包括台式机、移动设备和平板电脑。这使您能够很好地了解用户正在使用的设备类型。定期检查这些数据非常重要,因为新设备类型的当前采用率可能会频繁变化。

谷歌分析的设备信息部分的第二部分允许你确定你的用户使用哪些设备访问你的网站。要查看这些信息,请访问“设备”页面中“移动设备”下的“受众”部分。在这个页面中,您可以很容易地看到访问您站点的不同设备,包括来自每个设备的用户百分比的统计数据。图 8-6 中的截图向您展示了设备视图,默认情况下,该视图显示按每台设备的访问次数排序的设备,最常见的设备显示在最前面。谷歌在这个视图中包含了一个小彩蛋,如果你点击设备名称旁边的小相机图标,你会看到设备的图像。图 8-6 显示了我的网站的谷歌分析设备列表视图。

A978-1-4302-6695-2_8_Fig6_HTML.jpg

图 8-6。

Breakdown of the devices used to access my site as shown in Google Analytics

了解用户使用的浏览器和设备对开发人员来说非常重要,但是正如本章所强调的,开发人员也需要考虑用户浏览网站的过程。规划你认为用户将如何浏览一个网站是很重要的,但是你也需要能够衡量这一点,看看你的期望是否与现实相符。这就是谷歌分析行为流工具特别有用的地方。

如图 8-7 所示,行为流工具从左侧开始,显示用户到达我的网站时所登陆的页面。向左移动可以显示用户继续浏览网站的位置,下降的是在该点离开网站的用户数量。

A978-1-4302-6695-2_8_Fig7_HTML.jpg

图 8-7。

The Behavior Flow of a site shown in Google Analytics

在了解了用户如何使用你的网站后,你可能会想尝试做些改变来改进它。很难确定对网站的改进会给用户带来更好的体验;然而,Google Analytics 能让你做的是创建一个实验,向用户展示一个页面的多个版本之一。这与脸书和 Twitter 等社交网络向一小部分用户推出新功能,以试验用户如何与他们互动的方式类似。然后,成功的实验会在整个站点铺开,不太成功的实验会被丢弃或查看,以了解它们出错的原因,并在做出更改后重新运行。

当在 Google Analytics 中使用实验时,您可以点击 Create experiment,这将带您进入实验创建屏幕。我的网站的实验列表页面如图 8-8 所示。

A978-1-4302-6695-2_8_Fig8_HTML.jpg

图 8-8。

The experiments listing page in Google Analytics

然后,系统会提示您输入实验的名称,定义一个目标,然后选择要用于实验的流量百分比。重要的是要注意,你选择的流量百分比完全取决于你在进行实验时期望你的网站获得的访问者数量。你要保证你从实验中得到足够好的样本量,但是,你也要保证你把实验的风险降到最低。百分比越低,风险越低。还有一些额外的高级选项,允许您定义实验将持续多长时间,以及如何在实验期间分配流量。图 8-9 显示了如何创建实验。

A978-1-4302-6695-2_8_Fig9_HTML.jpg

图 8-9。

Creating an experiment in Google Analytics

在您设置了实验的目标之后,您可以单击“下一步”按钮,并且您将能够开始配置它。要配置实验,请输入您想要实验的原始页面的 URL,然后您可以添加多达九种页面变体(图 8-10 )。您可以为每个变体命名,并且需要包含一个直接指向每个变体的链接。

A978-1-4302-6695-2_8_Fig10_HTML.jpg

图 8-10。

You can configure multiple variations for your experiment

设置好变体并单击“下一步”按钮后,您可以选择手动将实验所需的代码插入到您的网页中,或者通过电子邮件发送给网站管理员。因为您是开发人员,所以您只想从文本框中复制代码(如图 8-11 所示)并粘贴到您的页面中。

A978-1-4302-6695-2_8_Fig11_HTML.jpg

图 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 会话。您可以完全控制如何播放用户的会话,能够播放、滚动视频以及以不同的速度回放。

A978-1-4302-6695-2_8_Fig12_HTML.jpg

图 8-12。

Playback of a ClickTale user session

除了记录浏览网站的用户,ClickTale 还可以利用这些数据生成热图。热图是数据的图形表示,显示数据中的相关性,在这种情况下,这就是用户如何与网站交互。ClickTale 允许您查看一系列不同的热图,每个热图关注不同的指标:鼠标移动、用户点击的位置、用户关注的位置以及用户通常在页面上滚动到的位置。

要在 ClickTale 中查看热图,只需从主仪表板中选择您想要查看的热图。在图 8-13 中,我选择了鼠标移动热图,正如所料,您可以看到用户移动鼠标的主要区域是主导航。您还会注意到,在导航上的链接周围有一个小方框,下面有一个百分比。这是点击这些链接的用户的百分比。

A978-1-4302-6695-2_8_Fig13_HTML.jpg

图 8-13。

The heatmap view of ClickTale

与谷歌分析类似,ClickTale 允许你观察用户如何浏览网站,这是通过他们的转换漏斗实现的。ClickTale 的实现相对于 Google Analytics 的好处在于,它允许你更容易地进行过滤,而不必进行大量的配置,并且它专注于在达到最终目标之前查看用户退出你的网站的位置。界面也更简洁,更容易使用。

要开始使用转换漏斗(如图 8-14 所示),请转到仪表板并向下滚动到转换漏斗部分,单击查看/编辑漏斗链接,或者使用侧栏菜单并选择转换漏斗。

A978-1-4302-6695-2_8_Fig14_HTML.jpg

图 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 )。

A978-1-4302-6695-2_9_Fig1_HTML.jpg

图 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";

}

}

};

下一步是定义当您进入每个状态时将被调用的方法。因为您已经定义了三个状态,所以您将创建三个方法:enableSmallenableMediumenableLarge。对于本例,只需将状态的名称记录到控制台:

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,对于onEnteronResizeonLeave方法,您只需将状态当前正在做的事情记录到控制台。

当您设置好所有的状态后,您需要告诉 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)。您将把它作为第一个参数传递,对于选项,让我们传递一个matchunmatch方法,两者都记录到控制台。放在一起,看起来是这样的:

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 所示。

A978-1-4302-6695-2_9_Fig2_HTML.jpg

图 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 所示。

A978-1-4302-6695-2_9_Fig3_HTML.jpg

图 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">&times;</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 所示。

A978-1-4302-6695-2_9_Fig4_HTML.jpg

图 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 所示),这在沿途的几个点上引入了延迟。

A978-1-4302-6695-2_10_Fig1_HTML.jpg

图 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 所示。

A978-1-4302-6695-2_10_Fig2_HTML.jpg

图 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。

A978-1-4302-6695-2_10_Fig3_HTML.jpg

图 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 所示的窗口。

A978-1-4302-6695-2_10_Fig4_HTML.jpg

图 10-4。

The ImageOptim tool, drag your images onto the window to compress them

要压缩图像,只需将想要压缩的图像拖到窗口的中央。将图像放入该工具后,它将开始压缩您的图像。当它完成一个图像,一个小勾会出现在左边的行和信息提供了多少压缩节省。如图 10-5 所示。

A978-1-4302-6695-2_10_Fig5_HTML.jpg

图 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 中展示了这一点。

A978-1-4302-6695-2_10_Fig6_HTML.jpg

图 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 )。

A978-1-4302-6695-2_10_Fig7_HTML.jpg

图 10-7。

“srcset” being used to define the widths of our images

这就是事情开始变得有点复杂的地方,尽管你已经告诉浏览器每张图片有多大,但你还没有告诉浏览器在任何给定点图片有多大。这就是sizes属性的用武之地。使用sizes属性,您可以告诉浏览器,对于任何给定的视窗大小,您期望图像的大小是多少。你可以使用我们在第三章中作为媒体查询的一部分使用的相同媒体表达来实现这一点。对于每个媒体表达式,我们还指定当媒体表达式为真/匹配时图像的宽度。如果您指定一个不包含媒体表达式的宽度值,当没有媒体查询匹配时,该宽度将作为图像的默认宽度(参见图 10-8 )。

A978-1-4302-6695-2_10_Fig8_HTML.jpg

图 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。

设置了srcsetsizes属性后,您的浏览器就能够自动确定最适合加载的图像。在这样做时,浏览器可以考虑设备的许多特性,这些特性包括显示器的像素密度、图像相对于视口宽度的大小、用户偏好(用户可以选择加载较低质量的图像以减少数据消耗)以及用户连接的网络类型。一个完成的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 所示。

A978-1-4302-6695-2_10_Fig9_HTML.jpg

图 10-9。

Our small image being loaded on the small viewport size

通过只下载一个较小的图像到较小的设备上,我们减少了页面的文件大小,这有助于提高我们网站在较小设备上的性能。虽然这可以通过使用scrset,来实现,但是picture元素增加的冗长性允许我们选择在哪个设备上显示哪个图像,而不是让浏览器来选择。这使我们能够根据设备的大小灵活地改变图像的艺术方向。看了在较小的视窗中加载我们的站点时会发生什么,我们现在将在一个中等大小的视窗中加载我们的页面,如图 10-10 所示。

A978-1-4302-6695-2_10_Fig10_HTML.jpg

图 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属性的问题。相反,这是一个为你的具体情况选择你需要的详细程度的问题。

如果你只是以不同的分辨率提供相同的图像,这样你的网站就能提供最佳的图像,允许浏览器选择最适合浏览器环境的图像,那么使用srcsetsizes属性会很好。但是,如果您想将不同的图像指向特定的视窗尺寸,并完全控制图像显示的视窗尺寸,那么您应该选择使用picture元素将图像指定为source元素。

我们也可以选择使用srcsetsizes属性为每个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 所示。

A978-1-4302-6695-2_10_Fig11_HTML.jpg

图 10-11。

The Pingdom Website Speed Test, just enter your URL and click Test Now

单击“立即测试”按钮后,该工具将分析站点并发现任何潜在的性能问题。开始运行测试后,你会看到一些关于你的站点的基本性能信息(如图 10-12 所示),包括你的站点发出的 HTTP 请求的数量、加载时间和你的页面大小。

A978-1-4302-6695-2_10_Fig12_HTML.jpg

图 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

A978-1-4302-6695-2_10_Fig13_HTML.jpg

图 10-13。

The Waterfall of the performance of the site shown in Pingdom

绩效评分

Pingdom 还提供了查看不同性能领域的个人性能分数的能力。作为开发者,这可以让你很快看到你的网站做得很差的地方,这样你就可以纠正它们,因为如果你修复它们,这些地方可能会给你带来最高的性能收益。我的博客( www.jonathanfielding.com )的表现评分如图 10-14 所示。

A978-1-4302-6695-2_10_Fig14_HTML.jpg

图 10-14。

Pingdom performance scoring for my blog

页面分析

Pingdom 还提供了一些进一步的页面分析数据,这些数据在查看站点性能问题时非常有用。它提供的数据可以分为三类:加载时间分析、大小分析和请求分析。我的博客的完整页面分析如图 10-15 所示。

加载时间分析是与页面加载时间相关的数据;它包括站点在瀑布图中显示的每个状态下花费的时间,加载每个内容类型花费的时间,以及从每个域加载花费的时间。后者可能特别有用,因为它允许您轻松地看到由第三方服务(如您网站上包含的广告)导致的页面加载瓶颈。

大小分析数据提供了页面权重来源的详细分类。特别是,它显示了每种内容类型的大小,这表明哪种类型的内容最大。它还显示了从其他域提供的内容的大小,当您试图确定为什么您的站点看起来很大,而您觉得自己的文件大小合适时,这很有用。

请求分析信息与您的站点必须发出的请求数量有关。这分为两种类型的数据:每个内容类型的请求数和每个域的请求数。同样,这允许您查看第三方插件是否正在创建您可能没有意识到的过多的 HTTP 请求。

A978-1-4302-6695-2_10_Fig15_HTML.jpg

图 10-15。

Pingdom full page analysis

进行优化

YSlow ( https://developer.yahoo.com/yslow/ )是雅虎开发的工具这允许你测试你的网页的性能。它可以作为浏览器插件安装,这意味着您可以在自己的浏览器中直接测量网页的性能。

要开始使用 YSlow,请访问 http://yslow.org/ ,在那里您可以选择您的浏览器并安装 YSlow 插件的相关版本。安装完成后,访问您的网站并单击浏览器中添加的 YSlow 图标。图标如图 10-16 所示。

A978-1-4302-6695-2_10_Fig16_HTML.jpg

图 10-16。

Chrome toolbar, the YSlow icon is highlighted

单击 YSlow 图标后,会出现一个弹出窗口,向您提供 YSlow 的一些信息。如果您的站点阻止自己嵌入 iframe,那么您需要确保选中了此页面上的复选框。要运行 YSlow 测试,只需点击运行测试按钮,如图 10-17 所示。

A978-1-4302-6695-2_10_Fig17_HTML.jpg

图 10-17。

The YSlow panel

一旦 YSlow 运行了它的测试,你就可以浏览每一个标签来决定你的站点运行的如何。

级别

一旦 YSlow 运行了它的测试,你将被带到 Grade 选项卡,在这里 YSlow 将根据你的站点的性能给它打分。它还给出了你在 23 个不同领域的分数,你可以点击这些分数来获得关于你为什么得到那个分数以及你可以做什么来提高它的更多信息。在我的博客上运行测试后的成绩视图如图 10-18 所示。

A978-1-4302-6695-2_10_Fig18_HTML.jpg

图 10-18。

The YSlow Grade panel for my blog

成分

YSlow 的 Components 选项卡提供了关于组成站点的各个资源的信息。它们按类型排序,您可以通过扩展类型来查看各个文件,从而找到关于每个资源的更多信息。

查看单个文件时,您可以看到有关文件大小、文件头以及随文件发送和接收的 cookies 的信息。这可能是有用的,因为这意味着你可以很容易地看到,如果你发送大量的 cookie 数据与您的文件,所以你可以解决这个问题,同时优化您的网站。我的博客的组件信息如图 10-19 所示。

A978-1-4302-6695-2_10_Fig19_HTML.jpg

图 10-19。

The YSlow Components panel for my blog

统计数字

YSlow 的 Statistics 选项卡提供了一些图表,显示了每种内容类型的请求所占的百分比,以及每种内容类型的 HTTP 请求数量。我的博客的统计面板如图 10-20 所示。

A978-1-4302-6695-2_10_Fig20_HTML.jpg

图 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 所示。

A978-1-4302-6695-2_10_Fig21_HTML.jpg

图 10-21。

Initial start page of WebPageTest, with the option to select browser and advanced options

因为 WebPageTest 站点是一个受欢迎的服务,所以您可能需要排队等候才能使用它。然而,通常你不用等超过几分钟。

一旦加载了结果,与其他测试类似,WebPageTest 会给你的站点一个性能等级。此外,该页面还显示了关于在第一次查看和重复查看时加载网站所用时间的信息。我的博客的绩效等级如图 10-22 所示。

A978-1-4302-6695-2_10_Fig22_HTML.jpg

图 10-22。

The WebPageTest performance grade for my blog

瀑布景观

运行完这个测试后,您可以单击显示的瀑布图像,进入瀑布视图。该视图显示了加载每个文件所用时间的详细信息,包括 DNS 查找、初始连接、SSL 协商、首字节时间和内容下载。

这个视图非常有用,可以让您看到资产加载受阻的任何地方,或者资产转移的任何问题。我的博客的瀑布视图结果如图 10-23 所示。

A978-1-4302-6695-2_10_Fig23_HTML.jpg

图 10-23。

The WebPageTest Waterfall View after testing my blog

高级测试

WebPageTest 在衡量网站性能的工具中真正脱颖而出的地方在于它的可配置性。我的意思是,在设置您的性能测试时,您可以从几个高级选项中选择各种选项。

要选择这些高级选项,您只需单击“高级设置”链接,这将展开站点的这一部分,以便您可以选择所需的高级选项。高级选项分为以下选项卡:

Test Settings   Advanced   Chrome   Auth   Script   Block   SPOF

虽然我不会详细解释所有这些选项,但最重要的设置之一是第一个(测试设置)选项卡上显示的连接设置。通过允许您选择不同的连接速度,此设置使您作为开发人员能够在用户可能面临的条件下测试网站。可供选择的范围从快速电缆连接到 56K 调制解调器。当决定测试哪些速度时,你需要对你的目标用户可能使用的速度持现实态度。用于选择这些高级选项的菜单如图 10-24 所示。

A978-1-4302-6695-2_10_Fig24_HTML.jpg

图 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 所示。

A978-1-4302-6695-2_10_Fig25_HTML.jpg

图 10-25。

Google Analytics Site Speed Overview

除了总体概述,Google Analytics 还能够就如何提高页面性能给出一些建议。这可以在网站速度建议页面上找到。初始视图包括一个页面列表,其中有一个标题为“速度建议”的列,每行都有一个链接,您可以单击该链接查看该页面的建议(如图 10-26 所示)。

A978-1-4302-6695-2_10_Fig26_HTML.jpg

图 10-26。

Google Analytics site Speed Suggestions

摘要

本章讨论了优化网站性能的方法。因为性能是一个很大的主题,所以我把重点放在了对一个响应性网站有最大影响的方面。

首先讨论的领域之一是网络性能的重要性,现在您应该对延迟和带宽以及它们如何影响您站点的网络性能有了很好的理解。然后,我讨论了如何通过使用允许您减少 HTTP 请求和优化服务器设置的技术来优化您的站点,以最好地应对这些限制。

然后,我解释了如何通过使用服务器端技术来确定向站点用户提供什么服务,从而优化站点性能。这包括查看如何使用设备描述库来确定用户正在使用的设备类型,以便为他们提供优化的内容。

虽然了解优化性能的方法使您能够使用性能响应技术来构建您的站点,但是能够度量性能也很重要。为了让您能够做到这一点,我介绍了两种您可以在站点开发过程中使用的工具:测量工具 WebPageTest、Pingdom 和 YSlow,以及生活工具,即在用户设备上测量站点性能的工具,特别是 Google Analytics 站点速度工具。

希望这本书能帮助你努力构建响应性网页。通过这本书,你将了解到从头开始创建一个网站或者改造一个现有网站的步骤。通过将这些技能应用到你的工作中,你将有助于改善用户在使用我们网站时的体验。

我真的希望你喜欢读这本书,就像我喜欢写这本书一样,并且当你想尝试不同的技术或改进你的响应网站时,你可以回头看看这本书作为参考。

Footnotes 1

http://httparchive.org/

2

http://www.webperformancetoday.com/2011/07/20/new-findings-mobile-web-users-are-more-disappointed-than-ever/

3

http://www.webperformancetoday.com/2013/12/11/slower-web-pages-user-frustration/

4

http://www.webperformancetoday.com/2011/07/20/new-findings-mobile-web-users-are-more-disappointed-than-ever/

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/ 找到它。

posted @ 2024-08-19 15:41  绝不原创的飞龙  阅读(86)  评论(0)    收藏  举报