乐者为王

Do one thing, and do it well.

移动化你的HTML5网站

英文原文:https://www.html5rocks.com/en/mobile/mobifying/

简介

现在开发移动Web是一个热门话题。今年以来,智能手机的销售量首次超过PC。越来越多的用户正在使用移动设备来访问Web,这意味着为移动浏览器优化网站变得至关重要。

对于大量开发者来说,“移动”战场仍是未知的领域。许多现有的遗留网站完全忽视移动用户。相反,这些网站主要为桌面浏览设计,因而在移动浏览器中效果不佳。本网站(html5rocks.com)也不例外。在推出时,我们只在该网站的移动版本上投入少许精力。

创建移动友好的html5rocks.com

作为一项练习,我认为给现有的html5rocks增加移动友好的版本将会很有趣。我主要关注针对目标智能手机所需的最低工作量。我的练习目标不是创建一个全新的移动网站并维护两个代码库。这是巨大的时间浪费且将永远消失。我们已经定义网站的结构(HTML)。我们有一个外观和感觉(CSS)。核心功能(JavaScript)也已存在。重点是,很多网站都以相同的方式存在。

本文将探讨如何创建针对Android和iOS设备进行优化的移动版html5rocks。只需在支持这些操作系统的设备上加载html5rocks.com即可查看其差异。没有重定向到m.html5rocks.com或其它这种性质的蠢事。你可以像使用html5rocks那样获得更多的好处,并且在移动设备上运行良好。

desktop-home mobile-home

桌面(左边)和手机(右边)上的html5rocks.com。

CSS媒体查询

HTML4和CSS2支持媒体相关的样式表已经有段时间了。例如:

1
<link rel="stylesheet" media="print" href="printer.css">

将针对打印设备并在打印时为页面内容提供特定的样式。CSS3将媒体类型的想法更进一步,并通过媒体查询增强其功能。媒体查询通过允许更精确地标记样式表来扩展媒体类型的有用性。这使得内容的显示可以针对特定范围的输出设备进行定制,而无需改变内容本身。听起来非常适合需要修改的现有布局!

你可以在外部样式表的media属性中使用媒体查询来定位屏幕宽度、设备宽度、方向等。有关完整列表,请参阅W3C媒体查询规范

目标屏幕大小

在以下的示例中,phone.css将应用于那些浏览器认为是“手持式”的设备或屏幕宽度<=320px的设备。

1
2
<link rel="stylesheet"
  media="handheld, only screen and (max-device-width: 320px)" href="phone.css">

给媒体查询加上前缀“only”关键词将导致非CSS3兼容浏览器忽略该规则。

以下内容将针对641px到800px之间的屏幕尺寸:

1
2
<link rel="stylesheet"
  media="only screen and (min-width: 641px) and (max-width: 800px)" href="ipad.css">

媒体查询也可以出现在内联的<style>标签中。当纵向显示时,以下内容针对all媒体类型:

1
2
3
<style>
  @media only all and (orientation: portrait) { ... }
</style>

media="handheld"

我们需要先讨论下media="handheld"。事实上,Android和iOS会忽略media="handheld"。宣称那样用户将会错过针对media="screen"样式表提供的高端内容,而且开发者不太可能维护较低质量的media="handheld"版本。因此,作为其“全网”格言的一部分,大多数现代智能手机浏览器都会忽略手持式样式表。

使用此功能来匹配移动设备是理想的选择,但各种浏览器都以不同的方式实现它:

  • 有些只读取手持式样式表。
  • 有些只读取手持式样式表(如果有的话),但默认为屏幕样式表。
  • 有些既读取手持式样式表,也读取屏幕样式表。
  • 有些只读取屏幕样式表。

Opera Mini不会忽略media="handheld"。让Windows Mobile识别media="handheld"的技巧是将屏幕样式表的媒体属性值大写:

1
2
3
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">

html5rocks如何使用媒体查询

媒体查询在整个移动html5rocks中被大量使用。它们允许我们调整布局而不必对我们的Django模板标记进行重大改变!另外,各种浏览器对它们的支持都非常好。

在每个页面的<head>中,你将看到以下样式表:

1
2
3
4
<link rel="stylesheet"
  media="all" href="/static/css/base.min.css" />
<link rel="stylesheet"
  media="only screen and (max-width: 800px)" href="/static/css/mobile.min.css" />

base.css始终定义html5rocks.com的主要外观和感觉,但现在我们正在为800px以下的屏幕宽度应用新样式(mobile.css)。其媒体查询涵盖智能手机(~320px)和iPad(~768px)。效果:我们在base.css中逐渐地覆写样式(只在必要时),以使内容在移动设备中看起来更好。

mobile.css强制执行的一些样式改变:

  • 减少整个网站的额外空白/填充。小屏幕意味着空间非常重要!
  • 移除:hover状态。他们将永远不会在触摸设备上被看到。
  • 将布局调整为单列。更多的稍后再说。
  • 移除网站主容器div周围的box-shadow。大的盒阴影会降低页面性能。
  • 使用CSS弹性盒模型box-ordinal-group来改变主页上每个部分的排序。你会注意到主页上的“LEARN BY MAJOR HTML5 FEATURE GROUPS”在“TUTORIALS”部分之前,但在移动版本上则是之后。这种排序对移动版本更有意义,并且不需要改变标记。
  • 移除opacity的变化。在移动设备上改变alpha值会出现性能损失。

移动元标记

Mobile WebKit支持一些好东西,它们可以让用户在某些设备上获得更好的浏览体验。

视口设置

第一个元设置(以及最常用的设置)是视口属性。设置视口是告诉浏览器,内容应该如何适应设备的屏幕,并通知浏览器该网站已针对移动设备进行过优化。例如:

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">

告诉浏览器将视口设置为初始比例为1的设备宽度。该示例还允许缩放,这对于网站而言可能是理想的,但对于Web应用则不是。我们可以通过user-scalable=no防止缩放,或者将缩放限制在某个级别:

1
2
<meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">

注意:width也可以采用像素值。在iPhone和其它一些智能手机上,设置width=320可以达到与width=device-width相同的效果。请记住,不是所有的手机都具有此确切宽度,因此最好坚持使用device-width,让浏览器计算剩余的宽度。

Android扩展了视口元标记,允许开发者指定网站使用的屏幕分辨率:

1
<meta name="viewport" content="target-densitydpi=device-dpi">

target-densitydpi的可能值是device-dpi、high-dpi、medium-dpi、low-dpi。

如果要为不同的屏幕密度修改网页,请使用CSS媒体查询-webkit-device-pixel-ratio或JavaScript的window.devicePixelRatio属性,然后将target-densitydpi元属性设置为device-dpi。这会阻止Android在你的网页上执行缩放,并允许你通过CSS和JavaScript对每个密度进行必要的调整。

有关针对设备分辨率的更多信息,请参阅Android的WebView文档

全屏浏览

还有另外两个元值是iOS特定的:apple-mobile-web-app-capable和apple-mobile-web-app-status-bar-style将把页面内容渲染成类似应用程序的全屏模式,并使状态栏变为半透明:

1
2
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

有关所有可用元选项的更多信息,请参阅Safari参考文档

主屏图标

iOS和Android设备也接受链接rel="apple-touch-icon"(iOS)和rel="apple-touch-icon-precomposed"(Android)。当用户收藏你的网站时,它们会在用户的主屏上创建一个显眼的类似应用程序的图标:

1
2
3
4
<link rel="apple-touch-icon"
      href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
      href="/static/images/identity/HTML5_Badge_64.png" />

html5rocks如何使用元标记

把所有东西放在一起,这里是html5rocks的<head>部分的一个片段:

1
2
3
4
5
6
7
8
9
10
11
<head>
  ...
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />

  <link rel="apple-touch-icon"
        href="/static/images/identity/HTML5_Badge_64.png" />
  <link rel="apple-touch-icon-precomposed"
        href="/static/images/identity/HTML5_Badge_64.png" />
  ...
</head>

垂直布局

在较小的屏幕上,垂直滚动要比水平滚动方便得多。将内容保留在单列中,垂直布局对于移动设备是首选。对于html5rocks,我们使用CSS3媒体查询来创建这样的布局。而且,也不需要改变标记。

tutorials details features profiles

整个网站的单列垂直布局。

移动最优化

我们所做的大多数优化都是应该首先完成的事情。诸如减少网络请求数量、JS/CSS压缩、gzip压缩以及最小化DOM操作等。这些技术是常见的最佳实践,但在匆忙将网站上线时会被忽略。

自动隐藏地址栏

移动浏览器的屏幕实际可用面积不像桌面浏览器的那么大。在不同平台上,有时最终会在屏幕顶部显示一个大大的地址栏……即使在页面加载完成之后,这使得事情变得更糟。

处理它的一个简单方法是使用JavaScript滚动页面。即使只是滚动1个像素也会清除讨厌的地址栏。为强制隐藏html5rocks的地址栏,我将一个onload事件处理器绑定到window对象上,并将页面垂直滚动1个像素:

address-bar-mobile

丑陋的地址栏占用屏幕实际可用面积。

1
2
3
4
5
6
{ % if is_mobile % }
  // Hides mobile browser's address bar when page is done loading.
  window.addEventListener('load', function(e) {
    setTimeout(function() { window.scrollTo(0, 1); }, 1);
  }, false);
{ % endif % }

我们还使用is_mobile模板变量作为这个监听器的开关,因为它在桌面版本上不需要。

减少网络请求,节省带宽

众所周知,减少HTTP请求数量可以大大提高性能。移动设备进一步限制了浏览器可以进行的并发连接数量,因此移动网站从减少这些无关请求中受益匪浅。此外,削减每个字节至关重要,因为带宽限制通常在手机上。你可能会花费用户的钱!

以下是我们为减少网络请求和减少html5rocks带宽所采取的一些方法:

  • 删除iframe——iframe很慢!我们的大量延迟来自教程页面上的第三方共享小部件(Buzz、Google Friend Connect、Twitter、Facebook)。这些API通过<script>标签引入,并创建降低页面速度的iframe。这些小部件在移动版上已被移除。
  • display:none——在某些情况下,如果标记不符合移动配置,我们会隐藏它。一个很好的例子是主页顶部的四个圆角框:

box-icons

主页上的框按钮。

他们会在移动网站中消失。但请务必记住,浏览器仍然会为每个图标发出请求,尽管它们的容器被display:none隐藏。因此,仅仅隐藏这些按钮是不够的。这不仅会浪费带宽,而且用户甚至不会知道它在浪费带宽!解决方案是在我们的Django模板中创建一个“is_mobile”布尔值,有条件地忽略HTML的各个部分。当用户在智能设备上查看该网站时,这些按钮将被忽略。

  • 应用程序缓存——这不仅可以为我们提供离线支持,还可以创建更快的启动。
  • CSS/JS压缩——我们使用YUI压缩器取代Closure编译器,主要是因为它同时处理CSS和JS。
  • 尽可能使用CSS图像精灵。
  • 使用pngcrush进行图像压缩。
  • 对小图像使用了dataURI。Base64编码为图像增加大约30%多的大小,但节省了网络请求。
  • 使用单个脚本标记自动加载Google自定义搜索,而不是使用google.load()动态加载它。后者会产生额外的请求。
1
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"></script>
  • Modernizr被包含在每个页面内,即使它们从未被使用过。Modernizr非常棒,但它对每次加载都进行一系列的测试。其中一些测试会对DOM进行昂贵的修改并降低页面加载速度。现在,我们只在实际需要的页面中包含这些库,这减少了2个请求:)

额外的性能调整:

  • 将所有的JavaScript代码移到页面的底部(在可能的情况下)。
  • 删除内联的<style>标签。
  • 缓存DOM查找和最小化DOM操作——每次触及DOM时,浏览器都会执行重排。移动设备上的重排成本更高。
  • 将浪费性的客户端代码移到服务端。具体来说,设置当前页面的导航样式:
1
2
3
4
5
6
7
8
9
var lis = document.querySelectorAll('header nav li');
var i = lis.length;
while (i--) {
  var a = lis[i].querySelector('a');
  var section = a.getAttribute("data-section");
  if (new RegExp(section).test(document.location.href)) {
    a.className = 'current';
  }
}
  • 具有固定宽度的元素被替换为易变的width:100%或者width:auto。

应用程序缓存

移动版本的html5rocks使用应用程序缓存来加速初始加载,并允许用户离线阅读内容。

在你的站点上实现AppCache时,不缓存清单文件非常重要(在清单文件本身中显式地声明或使用大量缓存控制头隐式地声明)。如果你的清单被浏览器缓存,它对调试来说将是一个噩梦。iOS和Android在缓存此文件方面做得非常好,但不提供像桌面浏览器那样快速刷新缓存的方便方法。

为防止我们的网站缓存清单,我们首先设置App Engine永不缓存清单文件:

1
2
3
4
5
- url: /(.*\.(appcache|manifest))
  static_files: \1
  mime_type: text/cache-manifest
  upload: (.*\.(appcache|manifest))
  expiration: "0s"

其次,我们在新清单完成下载时使用JavaScript API通知用户。系统会提示他们刷新页面:

1
2
3
4
5
6
7
8
window.applicationCache.addEventListener('updateready', function(e) {
  if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
    window.applicationCache.swapCache();
    if (confirm('A new version of this site is available. Load it?')) {
      window.location.reload();
    }
  }
}, false);

为节省网络流量,请让你的清单保持简单。也就是说,不要调用在你网站上的每个页面。只需列出重要的图像、CSS和JavaScript文件。你想做的最后一件事是强制移动浏览器在每个AppCache更新时下载大量资源,但是请记住,如果页面包含<html manifest="...">属性,浏览器会在用户访问时隐式地缓存它。

Comments