用非响应式设计构建跨端Web App
最后更新于:2022-04-01 19:43:59
写在前面的话:对于移动Web App来说,响应式设计相当的有价值,现在大家也正在逐渐的了解它。但是我认为它也有自己适用的范围,需要根据具体的场景来选择使用。正好最近业界对此也有一些声音和反思传递,例如我之前的一篇博文《[用HTML5实现iPad应用无限平滑滚动](http://blog.csdn.net/hfahe/article/details/7535914)》里就有提及。现在我翻译一篇相关文章,大家可以对此有更多的了解和判断。-- 宇捷
**媒介查询很伟大,但是...**
对于Web开发人员来说,如果要通过对样式表进行微调来为不同尺寸设备的用户提供更好的体验,媒介查询(Media Queries)非常棒。 媒介查询实质上可以根据屏幕的尺寸来自定义网站的CSS。在你深入这篇文章之前,可以更多的了解[响应式网页设计](http://www.html5rocks.com/mobile/responsivedesign)(还记得《[用3个步骤实现响应式网页设计](http://blog.csdn.net/hfahe/article/details/7082718/)》这篇文章吗?) ,并且看看使用媒介查询的一些好例子: [mediaqueri.es](http://mediaqueri.es/) 。
像布拉德·弗罗斯特在[以前文章](http://bradfrostweb.com/blog/web/responsive-web-design-missing-the-point/)中指出的一样,调整界面只是构建移动Web App时需要考虑的众多事情之一。 如果你在构建移动Web App时只通过媒介查询自定义了布局,那么我们会有以下的问题:
- 所有的设备都采用同样的JavaScript、CSS以及内容(图片、视频等),导致产生比预期更长的加载时间。
- 所有的设备都有相同的初始DOM结构,可能迫使开发人员编写过于复杂的CSS样式。
- 对于为每个设备指定自定义的交互来说缺乏弹性。
**除媒介查询外,Web APP还需要更多**
不要误会我的意思。我并不是讨厌通过媒介查询进行响应式设计,并绝对认为它占有一席之地。此外,上述的一些问题可以通过例如[响应式图像](http://www.alistapart.com/articles/responsive-images-how-they-almost-worked-and-what-we-need/) ,动态脚本加载等来解决。但是就某一点而言,你也许会发现自己做了太多的增量调整,而可能提供不同的版本效果会更佳。
当构建的界面在复杂性方面有所增加,同时被单页的Web App所吸引,你会想要为每个设备类型自定义用户界面做更多事情。本文将教你如何用最少的努力实现这样的自定义。通用的方法包括将访问的设备划分到正确的分类,并且为该设备提供合适的版本,同时最大限度地提高代码在版本之间的重用。
**针对哪些设备类型?**
现在有成千上万的互联网设备,几乎每一个都有浏览器。复杂之处在于它们的差异性:苹果笔记本,Windows工作站,有触摸输入、滚轮、键盘和语音输入的iPhone手机,iPad和Android手机,带压力传感器的设备,智能手表,烤面包机以及冰箱等等。它们无处不在,而且有些非常罕见。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-09_57a9a2e76236e.jpg)
各种各样的设备([来源](http://www.flickr.com/photos/brad_frost/6164723945/in/set-72157627712478230/))
为了创造良好的用户体验,你需要知道谁是你的用户以及他们使用的是什么设备。如果你为桌面用户创建了一个使用鼠标和键盘的界面,并将它展示给智能手机用户,这将是一个极大的失败,因为它设计在另一个屏幕大小和输入方式之上。
这里有两种极端的方法:
1. 创建一个为所有设备工作的版本。用户体验将因此受到影响,因为不同的设备有不同的设计考虑。
2. 为每一个要支持的设备各自创建一个版本。这个工作将永远进行下去,因为你将为你的应用构建太多版本。此外,当新的智能手机诞生(大约每周都有)时,你将被迫再次创建一个版本。
这里有一个基本的权衡:有更多的设备类别时,你可以提供更好的用户体验,但是需要做更多设计,实现和维护的工作。
为每种设备创建单独的版本对于性能原因来说也许是一个好办法,或者你想为不同的设备创建的版本差异甚巨。否则, [响应式网页设计](http://www.html5rocks.com/mobile/responsivedesign)会是完全合理的做法。
**一个潜在的方案**
这里有一个妥协方案:将设备分类,并为每个种类提供最佳体验。选择什么类别取决于你的产品和目标用户。下面是一个示例,能够很好的跨越现今流行的网络设备。
1. 小屏幕+触摸(主要是手机)
2. 大屏幕+触摸(主要是平板)
3. 大屏幕+键盘/鼠标(主要是台式机/笔记本电脑)
这只是许多可能的分类方式之一,但在写作时有很大的意义。上面的列表中缺少的是没有触摸屏的移动设备(例如功能手机,一些专用的电子书阅读器)。然而,这些设备大部分都有键盘或者屏幕阅读软件,如果你的站点精心设计,可以在上面工作良好。
**特定外形的WEB App例子**
有许多针对不同因素提供不同Web服务版本的例子。谷歌搜索是这样,Facebook也是。这主要是考虑了到性能(获取资源,渲染页面)和更通用的用户体验。
在Native App的世界里,许多开发者选择为不同种类的设备设计产品。 例如,Flipboard iPad版本的UI与iPhone版本差异很大。平板版本为双手使用和水平旋转进行了优化,而手机版本关注单手交互和垂直翻转的体验。许多其他的iOS应用在手机和平板上也有明显的不同,例如[Things](http://culturedcode.com/things/)(Todo list)和如下所示的 [Showyou](http://showyou.com/) (社会化视频):
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-09_57a9a2e776307.jpg)
针对手机和平板定制的UI
**方法#1:服务器端检测**
在服务器端,我们要了解正在处理的设备有更多的限制。每次请求发送的User-Agent头所提供的user agent(用户代理)字符串可能是最有用的线索。正因为如此,相同UA的嗅探方法会产生作用。事实上,DeviceAtlas和WURFL项目已经开始这样做了(并提供了一大堆设备有关的其他信息)。
不幸的是目前这些项目都存在挑战。WURFL非常庞大,包含20MB的XML,可能为每个请求导致明显的服务器开销。有项目因为性能方面的原因分割了这些XML。DeviceAtlas并不开源,需要付费使用。
这里有更简单,而且免费的替代品,例如[检测移动浏览器](http://detectmobilebrowsers.com/)项目。当然,缺点是设备检测难免会不够全面。 此外,它只区分移动和非移动设备,以及通过[ad-hoc软件](http://detectmobilebrowsers.com/about)提供有限的平板支持。
**方法2:客户端检测**
使用特性检测,我们可以了解更多用户浏览器和设备的信息。我们需要确定的要点是,该设备是否具有触摸功能,以及它的屏幕是大是小。
我们需要画一条线来区分屏幕大和小的触摸设备。例如像5寸Galaxy Note的边缘情况。下图显示了许多流行的Android和iOS设备轮廓(附上相应的屏幕分辨率)。星号表示该设备可以支持双倍密度。虽然像素密度可能会增加一倍,CSS仍然会报告相同的大小。
对CSS里像素点的快速介绍:手机Web页面上的CSS像素和PC上并[不一样](http://www.quirksmode.org/blog/archives/2010/04/a_pixel_is_not.html)。iOS视网膜设备引入了双倍像素密度(例如iPhone 3GS对iPhone 4,iPad 2对iPad 3)。视网膜设备上Safari浏览器的用户代理仍然报告相同的设备宽度,以免破坏网页。当其它设备(例如Android)采用了更高分辨率的显示屏时,它们也采用了相当的解决方案。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-09_57a9a2e793d3c.jpg)
设备的分辨率
这种方式会更复杂,但是这对考虑同时兼容纵向和横向模式非常重要。我们不希望每次屏幕旋转时都重新加载页面或者加载额外的脚本,虽然我们可能要呈现不同的页面。
下图中,正方形代表每个设备的最大尺寸,是叠加了纵向和横向轮廓的结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-09_57a9a2e7a803a.jpg)
横向+纵向分辨率
通过将阈值设置为650px ,我们将iPhone,Galaxy Nexus分类为小触摸屏设备,而将iPad,Galaxy Tab分类为“平板”。跨界的Galaxy Note在这种情况下被归类为“手机”,将采用手机布局。
所以,一个合理的策略可能看起来像下面这样:
~~~
if (hasTouch) {
if (isSmall) {
device = PHONE;
} else {
device = TABLET;
}
} else {
device = DESKTOP;
}
~~~
赶快看看一个小的示例[特性检测方法](http://www.html5rocks.com/static/demos/cross-device/feature/index.html)吧。
另一种方法是使用用户代理嗅探来检测设备类型,基本上就是创建一套试探方法来匹配用户的navigator.userAgent。伪代码看起来像这样:
~~~
var ua = navigator.userAgent;
for (var re in RULES) {
if (ua.match(re)) {
device = RULES[re];
return;
}
}
~~~
马上来看看一个示例-[UA检测方法](http://www.html5rocks.com/static/demos/cross-device/ua/index.html)。
**在客户端加载的说明**
如果你正在服务器上检测用户代理,你可以在收到新请求时决定提供哪种CSS,JavaScript和DOM节点。然而,如果你正在采用客户端检测,情况则更为复杂。你有如下几种选择:
1. 重定向到特定设备类型的URL,其中包含该设备类型的版本。
2. 动态加载设备特定类型的内容。
第一种方法很简单,需要采用window.location.href = '/tablet'这种重定向的方式。然而,URL地址会附加设备类型的信息,所以你可能想使用HTML5的[历史API](http://diveintohtml5.info/history.html)来清理网址。不幸的是,这种方法涉及一个重定向,所以可能会很慢,尤其是在移动设备上。
第二种方法实现更加复杂。你需要一种机制来动态加载CSS和JS,还有(根据浏览器而定)你可能无法实现例如自定义 这样的事。此外因为没有重定向,你需要在一张页面上来响应请求。当然,你可以用JavaScript来实现,但是这可能导致性能缓慢和/或糟糕的代码,这一切都取决于你的应用程序。
**选择客户端或服务器方案**
下面是在这些方法之间的权衡:
**选择客户端** :
- 基于屏幕尺寸或可扩展性的方案和用户代理比起来更为长远。
- 无需不断更新用户代理名单。
**选择服务器** :
- 能完全控制什么设备上加载什么版本。
- 更好的性能:无需客户端重定向或动态加载。
我个人的偏好是,最开始使用device.js和客户端检测。 随着应用的发展,如果发现客户端重定向有明显的性能问题,你可以很容易地删除device.js脚本,并在服务器上执行用户代理检测。
**DEVICE.JS介绍**
device.js是一个起点,这样做基于语义,依靠媒介查询进行设备检测,从而无需特殊的服务器端配置,节省了需要实现用户代理字符串解析的时间和精力。
这个方法是在标签的顶部用搜索引擎友好的标记([linkrel=alternate](http://blog.whatwg.org/the-road-to-html-5-link-relations#rel-alternate))声明你要提供的网站版本。
~~~
~~~
接下来,你可以采用服务器端UA检测和版本重定向,或者使用device.js脚本来执行基于功能的客户端重定向。
更多详细信息,请参阅[device.js项目](https://github.com/borismus/device.js)页面 ,同时还有一个使用了device.js进行客户端重定向的[测试应用](http://borismus.github.com/device.js/sample/)。
**建议:MVC的具体视图**
现在你可能会想,我告诉你的是建立三个完全独立的应用程序,每个用于一种设备类型。不! 代码共享是关键。
希望你已经使用了一个类MVC的框架,例如Backbone,Ember等等。如果你已经熟悉重点分解的原则,尤其是你的用户界面(视图层)应该与逻辑(模型层)分离。如果你对此还比较陌生,可以开始了解[MVC的一些资源](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)和[JavaScript中的MVC](http://addyosmani.github.com/todomvc/) 模式。
跨设备非常适合现有的MVC框架。你可以轻松地移动视图到独立的文件,为每个设备类型创建一个自定义视图。然后你就可以为所有设备使用除了视图层之外同样的代码。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-09_57a9a2e7be931.jpg)
跨设备的MVC模式
你的项目可能有以下的结构(当然,你可以自由选择对你应用最有意义的结构):
~~~
models/ (shared models)
item.js
item-collection.js
controllers/ (shared controllers)
item-controller.js
versions/ (device-specific stuff)
tablet/
desktop/
phone/ (phone-specific code)
style.css
index.html
views/
item.js
item-list.js
~~~
这种结构使你能够完全控制每个版本加载哪些内容,因为你必须为每个设备采用自定义的HTML,CSS和JavaScript。这非常强大,是开发跨端Web App最精简和最有效的方式,不会依赖于一些小的技巧,例如自适应图像。
一旦你运行喜欢的构建工具,会把所有的Javascript和CSS合并和压缩到一个独立的文件里,以实现更快的加载速度,而输出的HTML页面看起来类似以下的形式(在手机上,使用device.js):
~~~
Mobile Web Rocks! (Phone Edition)
~~~
需要注意的是(touch-enabled: 0)媒介查询并不标准(只有Firefox通过moz前缀实现了),但是能够被device.js正确运行(感谢[Modernizr.touch](http://modernizr.com/))。
**版本覆盖**
设备检测有时候有误,在某些情况下,用户可能更喜欢在手机上采用平板布局(也许他们正在使用Galaxy Note),所以一定要向用户提供版本的选择。
通常的做法是提供一个从桌面到移动版本的链接。这很容易实现,device.js通过device的GET参数来支持此功能。
**结论**
综上,当需要建立跨设备单页的用户界面时,并不适合响应式设计,我们可以这样做:
1. 挑选一系列设备分类进行支持,并为设备分类设定标准。
2. 建立你的MVC应用,把界面从代码库分离出来。
3. 使用[device.js](https://github.com/borismus/device.js)进行客户端设备分类检测。
4. 当你准备好了时,为每个设备分类打包你的脚本和样式表。
5. 如果客户端重定向有性能问题,放弃device.js,并换到服务器端做UA检测。
译自:[http://www.html5rocks.com/en/mobile/cross-device/](http://www.html5rocks.com/en/mobile/cross-device/)
转载请注明:来自[蒋宇捷的博客](http://blog.csdn.net/hfahe)
';