简介
针对移动网站进行开发是当今的热门话题。今年,智能手机的销量首次超过了个人电脑。越来越多的用户使用移动设备浏览网页,这意味着,对于开发者来说,针对移动浏览器优化网站变得越来越重要。
对于许多开发者来说,“移动”战场仍然是一片未知的领域。 许多人现有的旧版网站完全忽略了移动用户。 相反,该网站主要针对桌面浏览进行了设计,在移动浏览器中表现不佳。本网站 (html5rocks.com) 也不例外。在发布之初,我们在移动版网站上投入的资源很少。
创建适合移动设备的 html5rocks.com
作为一项练习,我认为将 html5rocks(一个现有的 HTML5 网站)改造成适合移动设备的版本会很有趣。我主要关心的是,针对智能手机进行开发所需的工作量是否最少。我的练习目标不是创建一个全新的移动网站并维护两个代码库。这需要花费很长时间,而且是非常浪费时间的。我们已经定义了网站的结构(标记)。我们查看了外观和风格 (CSS)。核心功能 (JS)。我的意思是,很多网站都面临着同样的问题。
本文将介绍我们如何创建针对 Android 和 iOS 设备进行了优化的 html5rocks 移动版。只需在支持其中一种操作系统的设备上加载 html5rocks.com,即可看到差异。没有重定向到 m.html5rocks.com 或其他此类恶意网站。您可以直接使用 html5rocks,还可以获得额外的好处:在移动设备上呈现效果出色且运行良好。
CSS 媒体查询
HTML4 和 CSS2 已经支持与媒体相关的样式表一段时间了。例如:
<link rel="stylesheet" media="print" href="printer.css">
会定位到打印设备,并在打印页面内容时为其提供特定样式。 CSS3 进一步完善了媒体类型的概念,并通过媒体查询增强了其功能。媒体查询允许更精确地标记样式表,从而扩大了媒体类型的实用性。这样,您无需更改内容本身,即可针对特定范围的输出设备自定义内容的呈现方式。这非常适合需要修改的现有布局!
您可以在外部样式表的 media
属性中使用媒体查询来定位屏幕宽度、设备宽度、屏幕方向等。如需查看完整列表,请参阅 W3C 媒体查询规范。
定位屏幕尺寸
在以下示例中,phone.css
适用于浏览器认为是“手持设备”的设备,或屏幕宽度小于等于 320 像素的设备。
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
如果在媒体查询前面加上“only
”关键字,则会导致不符合 CSS3 标准的浏览器忽略该规则。
以下代码会定位到 641 像素到 800 像素之间的屏幕尺寸:
<link rel='stylesheet'
media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>
媒体查询也可以显示在内嵌 <style>
标记中。以下代码定位到纵向模式下的 all
媒体类型:
<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"
的诀窍是将屏幕样式表的媒体属性值转换为大写形式:
<!-- 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>
中,您会看到以下样式表:
<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 的主要外观和风格,但现在,我们将针对宽度小于 800 像素的屏幕应用新样式 (mobile.css
)。其媒体查询涵盖智能手机(约 320 像素)和 iPad(约 768 像素)。
效果:我们会逐步替换 base.css
中的样式(仅在必要时),以便在移动设备上获得更好的外观。
mobile.css
强制执行的一些样式更改:
- 减少网站上的额外空白/内边距。屏幕较小意味着空间非常宝贵!
- 移除了
:hover
状态。它们从未在触控设备上出现过。 - 将布局调整为单列。稍后我们会详细介绍这部分内容。
- 移除了网站主容器 div 周围的
box-shadow
。较大的边框阴影会降低页面性能。 - 使用了 CSS 弹性框模型
box-ordinal-group
来更改首页上每个部分的排序。 您会发现,“按主要 HTML5 功能组学习”位于网页版首页上的“教程”部分之前,但位于移动版首页上的“教程”部分之后。这种排序对移动设备而言更有意义,且无需更改标记。CSS flexbox 真棒! - 移除了
opacity
更改。更改 Alpha 值会降低移动设备上的性能。
移动元标记
Mobile WebKit 支持一些实用功能,可让用户在某些设备上获得更好的浏览体验。
视口设置
第一个元设置(也是您最常用的设置)是视口属性。设置视口可告知浏览器内容应如何适应设备屏幕,并告知浏览器网站已针对移动设备进行了优化。例如:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
告知浏览器将视口设为设备的宽度,初始缩放比例为 1。此示例还支持缩放,这对于网站来说可能很有用,但对于 Web 应用来说却不适用。我们可以使用 user-scalable=no
阻止缩放,或将缩放限制在一定水平:
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
Android 扩展了 viewport meta 标记,允许开发者指定网站是针对哪种屏幕分辨率开发的:
<meta name="viewport" content="target-densitydpi=device-dpi">
target-densitydpi
的可能值包括 device-dpi
、high-dpi
、medium-dpi
、low-dpi
。
如果您要针对不同的屏幕密度修改网页,请使用 -webkit-device-pixel-ratio
CSS 媒体查询和/或 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
将在类似于应用的全屏模式下呈现网页内容,并使状态栏变为半透明:
<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)作为链接。当用户将您的网站添加为书签时,这些图标会在用户的主屏幕上显示一个类似应用的醒目图标:
<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>
部分的代码段:
<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 媒体查询来创建此类布局。同样,无需更改标记。
移动优化
我们进行的大多数优化都是应首先完成的事情。例如减少网络请求数量、JS/CSS 压缩、gzip 处理(App Engine 上免费提供)以及最大限度地减少 DOM 操作。这些技巧是常见的最佳实践,但在匆忙发布网站时,有时会被忽略。
自动隐藏地址栏
移动浏览器的屏幕空间不如桌面浏览器。更糟糕的是,在不同的平台上,您有时会在屏幕顶部看到一个巨大的地址栏,即使网页加载完毕也是如此。
解决此问题的一个简单方法是使用 JavaScript 滚动页面。即使只向下移动一个像素,也能解决恼人的地址栏问题。为了在 html5rocks 上强制隐藏地址栏,我将 onload
事件处理脚本附加到 window
对象,并将页面垂直滚动了 1 像素:
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
我们还将此监听器封装为 is_mobile
模板变量,因为桌面设备不需要它。
减少网络请求,节省带宽
众所周知,减少 HTTP 请求数量可以显著提升性能。移动设备进一步限制了浏览器可以建立的并发连接数量,因此,减少这些多余请求对移动网站的益处会更大。此外,由于手机的带宽通常有限,因此尽可能减少字节数至关重要。您可能在让用户付费!
以下是我们为最大限度地减少网络请求并降低 html5rocks 上的带宽而采取的一些方法:
移除 iframe - iframe 速度缓慢!大量延迟来自教程页面上的第三方分享 widget(Buzz、Google Friend Connect、Twitter、Facebook)。这些 API 是通过
<script>
代码添加的,并会创建会降低网页速度的 iframe。移动版中移除了微件。display:none
- 在某些情况下,如果标记不适合移动版个人资料,我们会隐藏该标记。首页顶部的四个圆角框就是一个很好的例子:
移动网站上缺少这些链接。请务必注意,即使其容器已使用 display:none
隐藏,浏览器仍会针对每个图标发出请求。因此,仅隐藏这些按钮是不够的。这不仅会浪费带宽,而且用户甚至不会看到浪费带宽带来的好处!解决方案是在 Django 模板中创建一个“is_mobile”布尔值,以便有条件地省略 HTML 部分。当用户在智能设备上查看网站时,系统会忽略这些按钮。
应用缓存 - 这不仅为我们提供了离线支持,还加快了启动速度。
CSS/JS 压缩 - 我们使用 YUI 压缩工具(而非 Closure 编译器),主要是因为它同时处理 CSS 和 JS。我们遇到了一个问题,即内嵌媒体查询(出现在样式表内的媒体查询)在 YUI 压缩程序 2.4.2 中出错(请参阅此问题)。使用 YUI Compressor 2.4.4 或更高版本可解决此问题。
尽可能使用 CSS 图片精灵。
使用 pngcrush 进行图片压缩。
针对小图片使用了 dataURI。Base64 编码会使图片增加约 30% 以上的大小,但可以减少网络请求。
使用单个脚本代码自动加载 Google 自定义搜索,而不是使用
google.load()
动态加载。后者会发出额外的请求。
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- 我们的代码美化打印程序和 Modernizr 会包含在每个网页中,即使它们从未使用过也是如此。Modernizr 非常棒,但它会在每次加载时运行一系列测试。其中一些测试会对 DOM 进行代价高昂的修改并降低网页加载速度。现在,我们只在实际需要的网页上添加这些库-2 个请求 :)
其他性能调整:
- 将所有 JS 移至页面底部(如有可能)。
- 移除了内嵌
<style>
标记。 - 缓存 DOM 查询并尽量减少 DOM 操作 - 每次您触摸 DOM 时,浏览器都会执行重新流式传输。在移动设备上,重新流布局的开销更高。
- 将浪费的客户端代码分流到服务器。具体而言,用于设置当前页面的导航样式的检查:
js 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 设置为永不缓存清单文件:
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
其次,我们使用 JS API 在新清单下载完成后通知用户。系统会提示用户刷新页面:
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 页面(并且该页面包含 <html manifest="...">
属性)。