简介
为移动网络进行开发已成为当今的热门主题。今年,智能手机破除已售 PC 的销量创下了新高。越来越多的用户使用移动设备浏览网页,这意味着,针对移动浏览器优化网站对于开发者来说变得至关重要。
“移动”战场对很多开发者来说仍然是未开发的领域。 许多人现有的旧版网站完全忽略了移动用户。 该网站主要针对桌面设备浏览而设计,在移动浏览器中性能降级。此网站 (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
将应用于浏览器认为“手持”的设备或屏幕宽度 <= 320px 的设备。
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
为媒体查询添加“only
”关键字前缀将导致不符合 CSS3 规范的浏览器忽略该规则。
以下是定位 641px 到 800px 之间的屏幕尺寸:
<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 flex 框模型
box-ordinal-group
更改首页上每个部分的顺序。您会发现,在首页上,“通过主要的 HTML5 功能组学习”位于首页的“教程”部分之前,但在移动版中,该部分位于其后面。这种排序方式适用于移动设备,且无需更改标记。CSS Flexbox 棒极了! - 移除
opacity
项更改。更改 Alpha 值会在移动设备上影响性能。
移动元标记
移动 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 允许开发者指定网站是针对哪种屏幕分辨率开发的,从而扩展了视口元标记:
<meta name="viewport" content="target-densitydpi=device-dpi">
target-densitydpi
的可能值为 device-dpi
、high-dpi
、medium-dpi
、low-dpi
。
如果您想针对不同的屏幕密度修改网页,请在 JavaScript 中使用 -webkit-device-pixel-ratio
CSS 媒体查询和/或 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 滚动网页。即使按 1 个像素执行此操作,也能很好地处理烦人的地址栏。
为了在 html5rocks 上强制隐藏地址栏,我向 window
对象附加了一个 onload
事件处理脚本,并将页面垂直滚动一个像素:
// 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 非常慢!我们的很大一部分延迟来自教程页面上的第三方共享微件(Buzz、Google 朋友群、Twitter、Facebook)。这些 API 是通过
<script>
标记添加的,并创建会降低网页速度的 iframe。针对移动设备移除了这些微件。display:none
- 在某些情况下,我们会隐藏不适合移动设备的标记。首页顶部的四个圆角框就是一个很好的例子:
移动网站中缺少这些广告。请务必注意,浏览器仍会为每个图标发出请求,尽管它们的容器已通过 display:none
隐藏。因此,仅仅隐藏这些按钮是不够的。这样做不仅会浪费带宽,而且用户甚至不会看到因浪费带宽而收获成果!解决方案是在我们的 Django 模板中创建一个“is_mobile”布尔值,以便有条件地省略 HTML 的某些部分。
当用户在智能设备上浏览该网站时,这些按钮被略去。
Application Cache - 这不仅可以为我们提供离线支持,还可以加快启动速度。
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="...">
属性)。