书名:高性能响应式Web开发实战
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
• 著 李光毅
责任编辑 杨海玲
• 人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
• 读者服务热线:(010)81055410
反盗版热线:(010)81055315
李光毅 曾就职于爱奇艺,现任百度高级前端工程师,主要负责前端Web 产品开发, 以及后端Node.js 框架维护。 在HTML、JavaScript 等前端技术方面略有心得,喜欢对Web 性能吹毛求疵,同时也对ASP.NET、MongoDB、Python 等技术有着浓厚的兴趣。曾经业余时间喜欢折腾硬件编程,如Kinect for Windows、Leap Motion 等,现在爱好使用Unity 写游戏。热爱前端,乐于分享。目前在北京航空航天大学攻读交互式设计在职研究生。
响应式Web设计的理念是让页面根据用户行为以及设备环境(屏幕尺寸、分辨率等)进行相应的响应和调整。响应式网页设计就是一个网站能够兼容多种终端,而不是为每种终端做一个特定的版本。
本书分为两部分,第一部分是前端的基本响应式技术,涉及响应式布局、图片的处理、解决问题的思路以及一些进阶的技巧等;第二部分在以上内容的基础上,加入了对页面进行性能调优的内容,包括如何确立性能指标,如何使用不同的工具衡量性能,以及如何解决常规的性能问题等。
本书适合有兴趣学习响应式技术的前端从业人员和其他相关人员阅读。
作为一名程序员,写书也好,写博客也罢,其实都和写开源程序的性质是一样的,都是想要把自己的知识分享出去。分享是一件非常有成就感同时也是很快乐的事情,因为我们在此过程中会有很多新的想法,会迫不及待地想去实现,也会有很多人来和我们进行交流,探讨其他的一些可能性。最重要的是,对于做分享的人而言,做好分享很难!首先,分享者要对自己讲解的技术有足够的了解,不仅仅是了解如何用它,还要了解它的过去和未来;其次,分享者要能够娓娓道来,要站在受众的立场上考虑他怎样才能听懂,他可能会有哪些疑惑;最后,分享者有责任确保自己分享的知识的准确性和正确性,分享内容的质量同时也折射了分享者的技术水平,这也是迫使分享者进步的一个动力。
响应式技术,乃至前端的技术,发展是非常迅速的,现在能够使用或者可预见的响应式技术,在我看来是非常有意思和振奋人心的。但是,因为一些国内客观条件的限制(公司环境、从业者认知、用户行为等),响应式技术的发展与国外的发展水平有一定的差距。目前能找到的大部分与响应式设计相关的书基本都是从国外引进的,而这些原著一般是几年前出版的,因此这些书传授的知识现在看来显得有些保守和落后(当然终究有一天这本书的内容也会落伍和被淘汰,只是时间的问题罢了)。我想说,即使我们没有机会将大部分技术应用于实战,也应该通过一种渠道了解它究竟发展到何种程度了,至少在将来某一天需要时能够知道从哪里开始。
给页面做性能优化也是我这几年的工作内容之一。我阅读了很多资料,也做过很多的实践和尝试,踩过坑,也总结出一些经验,所以想把其中的一些宝贵经验分享出来。当然,这些经验不仅仅来源于我自己,还有来自工作中一起奋斗过的同事们,感谢他们。
我也是一个通过阅读来学习新技术的人,我会订阅一些技术博客,也会翻阅一些原版书籍。我更欣赏国外技术人员撰写的文章,因为他们讲解技术的时候总能做到循循善诱,有问题的起因,原有方案的不足,现有解决方案如何,以及在现有方案上又有谁做了哪些创新,现有方案仍然存在的不足,最后再提出一些开放性的问题,而不仅仅是给自己看的学习日记,或者把API文档更通俗地翻译一遍。
技术不是什么高深莫测的东西,一个看似复杂的解决方案拆解之后其实只是一些解决问题手段的叠加。因此,我一直希望在我给其他人分享技术时能有机会采用上面所说化繁为简的方式循序渐进。这本书就是这个理念的最好实践。
对于大多数国内开发者而言,响应式是一个即熟悉又陌生的词语。熟悉是因为它一直都在以各种方式影响着我们,陌生是有一部分人没有用过它。事实上,响应式技术已经是较为成熟的技术了。它有稳定的API作为支撑,有前人总结出的最佳实践作为开发指导,并且浏览器日趋完善的支持也保证了绝大多数用户都能无障碍访问,尽管可能大部分用户的浏览器环境或者我们的工作场景因为历史原因不允许我们自如地使用。
响应式设计的概念与HTML5类似,不是单指某一技术而是代指一些技术的集合。狭义上来说,媒体查询和响应式图片算是响应式设计的核心技能,但广义上看,任何能够让页面适配移动设备的技术都包含在内,甚至脚本和后端。当然,以本书的篇幅不可能把这一切都事无巨细地娓娓道来。响应式技术也像这个行业一样,始终不断地在更新迭代。本书的内容也只能涵盖现有技术的冰山一角。
第1章详细介绍本书的写作方向以及写作思想。这一章解释了许多的疑问:为什么需要响应式,为什么性能如此重要,究竟什么是响应式,以及在学习的过程中如何付诸实践。通过阅读这一章内容,读者会对本书的组织结构和选择题材的原由有所了解。
第2章并没有开始编码,而是了解在响应式开发中需要解决的问题,这些问题有助于理解接下来本书介绍的众多技术的意义,也从技术上区分了桌面前端开发与移动前端开发的差异,所以第2章也可以看作是第1章的延伸。
第3章与第4章可以作为一体。在这两章中,完成了一个拥有响应式基本布局的页面。在第3章中我们为布局做了一些准备工作,使用传统的前端技术搭建了一个简易的版本,为第4章埋下伏笔。第4章基于第3章完成的传统布局,逐个解决在移动端可能遇到的问题,通过引入媒体查询、伸缩布局、相对单位等,正式将布局“响应式化”。
第5章专注处理页面上的图片。图片虽然能够提高页面的吸引力,但它带来的副作用也不容小觑。在这一章中我们会尝试针对不同的屏幕和设备加载图片,甚至移除图片。在条件有限的情况下,我们也会尝试优化图片。
第6章和第7章介绍的是页面优化,第7章可视为第6章的进阶。第6章将帮助读者树立起对“性能”这个词的正确认识。我们将“性能”转化为实际数值从不同的维度进行衡量和追踪,同时通过一段常规代码初步了解脚本的一些性能瓶颈。在第7章中我们将会继续尝试一些其他的优化思路,如避免脚本、选择性加载甚至求助于后端,最后总结性能优化思路。
第8章主要介绍项目的维护问题。在这一章中读者将会了解如何简化上线和开发流程,降低工程的维护成本。
实战是本书的最大特点。整本书的内容由一个页面线索串起,也就意味着无论是学习动机还是要学习的技术,都从实际需求出发。同时本书还注重响应式与之前传统技术的对比,尽可能做到承上启下,便于大家理解。实战也意味着我们要考虑页面上线后可能存在的缺陷,如需要向下兼容、做线上优化等。
同时我们也正视了项目中的工程问题,借助于工具优化发布流程、降低维护成本等。
总而言之,希望读者在阅读完本书之后,可以对Web响应式设计有一个较为明确的了解,学到很多实用的知识,而不是在遇到相同的问题后只是觉得似曾相识而无从下手。
本章向读者大致描述整本书的轮廓。希望通过阅读本章内容,读者能够了解这本书涉及的技术范围、写作风格、写作思路以及贯穿全文的线索。我相信这对读者阅读接下来的内容会很有帮助,不至于让读者觉得某些章节的安排比较突兀。
当然,读者也可以跳过本章内容,直接进入下一章,开始实战技术的学习。
首先,我们先讲一下“为什么”需要响应式。
我不想再谈论移动设备的增长趋势,也不需要强调用户每天花费多少时间在移动设备上,更不必用数字和图表告诉各位移动互联网形势如何好。毕竟每天各种互联网报告和科技媒体都在反复提醒着我们这些事情。
这里我们仅站在产品和技术的角度上思考,假设没有响应式设计,假设不区分移动与桌面用户,任由他们访问相同的桌面端页面,会有什么问题?
以大众点评网为例,如果你真的在手机上访问过站点的桌面版,那体验将会是灾难般的,手机上网页文字很难辨别,如图1-1和图1-2所示。
图1-1
图1-2
当我想查看右下角的热门餐厅有哪些时,不得不小心翼翼地用手势放大、移动页面,调整到需要浏览的区域。请小心操作,因为稍不留神就可能误点击了页面的某一处链接导致浏览器跳转到其他页面去,又不得不返回,再重复之前的步骤(这是常常发生在我身上的事情)。
介于使用场景(如户外、室内、紧急程度等)和使用媒介(如手机、平板、电视甚至智能手表等)的不同,Web产品在受到诸多限制(如屏幕大小、交互方式)的不同终端上产品形态应当是存在差异的。
让我们再考虑一些更恶劣的情况,不,应该说更实际一些的情况。Web产品在移动设备上最大的天敌不是兼容性问题而是不稳定的网络信号。如果页面的体积过于庞大,请求过多,用户下载页面被中断而无法正常被访问的概率也就更大。大众点评(大部分网站也是如此)的移动版和桌面版在页面加载体积方面是有非常大区别的,如图1-3和图1-4所示。虽然这样的差异不一定是出于性能的考虑,但我仍然强烈建议尽可能压缩页面体积(可以通过利用浏览器缓存等方式)以减小风险,这样也能尽快向用户展示页面内容。
图1-3
图1-4
最后让我们再来看一组用于证明性能重要性的统计数据[1]:
对一个商业网站来说,时间就是金钱,用户没有理由把时间花在无法访问的网站上。
移动端浏览器的渲染效率、脚本执行效率与桌面端浏览器有一定的差距。页面上没必要向下兼容的冗余代码,以及更多无法预知的因素,都是在“想方设法”推迟着页面的展现。移动端面临的形势是严峻的,针对移动设备上的Web产品,应该在优化方面花更大的力气。
抛开产品本身,抛开商业因素,Web开发者的工作职责之一应该是用技术实现一个“好”的产品。一个网站没有CSS和JavaScript仍然可供浏览,移动设备浏览器当然也可以直接访问桌面端网页,但是这些情况下产品的可用性(usability)、可读性(readability)、可访问性(accessibility)如何保障呢?没有用户愿意历经艰难险阻才能使用产品。谈论响应式也好,移动化也罢,目标是让产品在移动端与桌面端一样好用,不仅仅是让布局变窄,让字体变小,让它看上去变得小了一号而已。这是机遇,也是挑战。
技术需要依靠产品来落地和彰显它的威力,否则再强悍的技术也只是象牙塔上小部分人的玩具而已。这就好比3A游戏大作对于游戏引擎的重要性(《孤岛危机》之于Cryengine,《战争机器》之于Unreal)。再有,“纸上得来终觉浅,绝知此事要躬行”,对古人如此,对程序员更是如此。学习技能和提升技能的最佳途径只有实践,开始使用学到的技术,并且遇到书本上从来就没有提及的困难,这才是进步的开始。
如果整本书每一章阐述的知识点相互独立,也就不能称之为书,至少不能称之为技术书,只能算作是某人博客的文章选集。所以书是有线索的,线索将每一章的内容联系起来,形成一个知识体系。例如,响应式图片与性能调优看似不相关,但过度地追求大而全的图片的解决方案,注定要用降低性能为代价。
我非常同意alistapart.com上的一篇文章《Building to Learn》[2] 中的一些观点,也深有感触:用技术做一些你感兴趣的事情,这是学习的最好方式。
综合以上原因,我们的书也需要一条线索,需要活生生的产品来将我们的知识付诸实践。这条线索就是完成一个可以应用在Jekyll(静态博客网站生成工具)上的响应式文章详情页。在每一章的结尾,我都将把这一章学到的技术运用到这个博客页面的制作过程中,来取代非响应式下的解决方案。读者读到书的结尾,这个页面也就开发完成了。读到这里的读者可以直接访问我的个人技术博客 http://qingbob.com来先睹为快,也可以访问本书在GitHub上的源码地址 https://github.com/hh54188/responsive-web-design-tutorial获取本书涉及的所有代码。
因为篇幅有限,所以整本书的主题是“开发”而不是“设计”。也就是说,我们只负责不遗余力地实现设计稿中的需求,而不问为何这样设计。要知道关于响应式设计同样也是一个庞大的课题。我相信已经有更好的文章和书描述了响应式产品、响应式交互式设计和移动优先,在本书中我只会偶尔提及。
大多数开发者在初学一门技术时常有的疑惑,用我常说的一个笑话来表达就是:当你把一个名词疑惑拿去向一个专家求解时,你的一个疑惑会变成三个疑惑,因为他会用另外两个你更加不了解的名词来解释这个名词。
上面的笑话也是我自己在为别人解释一些概念时常常陷入的怪圈。例如,你向一位非计算机专业的同学解释“在你输入网站地址的那一瞬间浏览器发生了什么”时,不免要牵扯到网络协议、浏览器引擎一类的专业词汇,而这些专业词汇又需要想方设法地用更通俗的概念进行讲解。
鉴于上述情形,本书采用与上述相反的叙述方式:先从简单的概念讲起。我不会在每一章开篇就讲有关这一章技术点的语法或者功能,而是先引入一个响应式设计中有待解决的问题,一个不涉及技术而用纯语言描述的场景(技术从来都不是深不可测的东西,它是为解决问题而生),然后围绕这个需求,先尝试使用常规的前端技术来解决,当然通常这样的解决方案并不够完美,接着要思考缺陷在哪儿,如何弥补,再引入那一章讲解的响应式技术,看看它是如何解决这个问题的。
但是还没有结束,新技术并不是万能的灵药,这把“利刃”也因为过于“锋利”而被人诟病。兼听则明——最后就要来听一些有关于这些技术的负面声音,并且思考它应该朝什么方向改进来弥补当下的不足。别忘了向前兼容,为不支持这些新技术特性的浏览器准备回滚方案。
我们有没有可能采用一种最直接的方式,用一句话阐述响应式在前端开发中究竟代指哪些技术?如果非要往前追溯对响应式技术的定义,一定要谈alistapart[3] 网站上的被奉为经典的两篇文章,即《Responsive Web Design》[4]和《A Dao of Web Design》[5] 。
在《Responsive Web Design》中,作者仅仅使用了流式布局(fluid layout)和媒体查询(media query)就完成了响应式页面的构建。那我们可不可以说,响应式技术就等于流式布局加上媒体查询?或者反过来说,如果一个站点没有使用流式布局或媒体查询,那么这个站点就不应该自诩使用了响应式设计?
这是不公平的,响应式设计应该是一类思考解决问题的方式而不是一成不变的技术集合。过去每当提到响应式技术时第一时间想到的只有流式布局和媒体查询,但就在我键盘上敲出这一段文字的当下,本书涉及的响应式图片技术与性能优化技巧,甚至后端的RESS概念,都也都被列入到响应式技术集合中,它们与媒体查询同样重要。但是,我们不能批评说只谈媒体查询和流式布局的人是狭隘的,技术仍然受限于整个时代水平的客观性。或许不久的将来又会有更具有前瞻性的技术让当下我们谈论的退出历史舞台,所以我们始终要以开放的心态和发展的眼光看待响应式。
引用梁文道杂文集《味道之第一宗罪》中的一篇谈食物正宗性的文章《正宗的传说》里的一段话:
坚持正宗根本违背了饮食文化的本性,饮食之道,就如人类的一切生活文化,总是在适应环境,总是在改变。欣赏美食要有好奇心,不能食古不化,死守祖训。
至少在这一方面,饮食和技术是一样的,没有所谓的正宗可言。
图1-5与图1-6给出的是本书中要完成的页面设计稿。
图1-5
图1-6
这是一位产品经理为本书而设计的。2015年年初sitepoint [6] 网站发布了一篇有关2015年网页设计趋势的文章《The Big Web Design Trends for 2015》 [7] 。文章中归纳了在2015年网页设计中将会出现的趋势性特征,如大气(make it big)、简约(minimize)、扁平设计(flat design),在这次设计稿中都得以体现。
图1-5所示为页面桌面端样式,图1-6所示为页面移动端样式。如何实现这两类样式,并且让这两种版样式的页面共存于同一套代码上,无缝、优雅地在不同设备间切换是本书要实现的需求。在正式开始之前,针对这个贯穿始终的需求,读者可能已经有了一些疑惑。
假设我们已用前端代码实现了上述功能,而代价却是过长的页面加载时间和顿卡,这是得不偿失的。但是功能的叠加与页面的性能负担却又是正比关系。这就需要我们对功能做取舍,对代码进行性能调优,这一类优化工作对移动端产品来说尤其重要。那么,对于如何进行调优读者可能又会有以下疑问。
所有这些问题,在本书中都会得到解答。
[1] http://www.guypo.com/17-statistics-to-sell-web-performance-optimization/
[2] http://alistapart.com/blog/post/building-to-learn
[4] http://alistapart.com/article/responsive-web-design
[5] http://alistapart.com/article/dao
[7] http://www.sitepoint.com/big-web-design-trends-for-2015/
响应式设计的主要工作就是要让网页适配当下种类繁多的设备,使页面在不同设备上仍然看上去友好并且可用。但是细想,当在设法让一个页面同时适配三星Galaxy S6和iPhone 6时,我们究竟是在适配什么?Galaxy S6和iPhone 6究竟存在哪些影响页面展现的差异因素?以上这些问题都可以归纳为:当谈论设备的时候我们究竟在谈论什么?
不同设备间的差异有很多种,我们不关心设备的制造厂商,不关心CPU功耗,不关心生产工艺,只关心会影响页面在屏幕上展现的设备因素。如果用户在来自两台不同厂商设备上浏览页面时的效果是一致的,那么从前端的角度讲,就可以认为在某种意义上这两台设备并无差异。本章将会让读者了解到,在响应式设计中,有哪些常用的差异性因素是需要考虑的,在本书的后面的章节中,主要也是围绕这些因素做适配与兼容。
图2-1所示的截图来自苹果中文网站对iPhone 6的一段技术规格描述,加灰底的文字部分的“PPI”即为本节所要讨论的内容。PPI这个概念的复杂之处在于,它的意义会随着上下文的改变而变得大相径庭,例如,它可以用于描述图片文件的某些属性,可以作为打印时的可配置参数。在这里我们只谈论它作为设备屏幕特征的情况。在本书后面的内容中,若无特殊说明,PPI都代指本含义。
图2-1
PPI(Pixel Per Inch)直译为“像素每英寸”。这样翻译其实有些晦涩,如果考虑到它实际想表达的意思,可以把它译为像素密度(在维基百科中,PPI这个名词也是归属于Pixel density [1])。和常常谈论的人口密度、建筑密度类似,表达的是某个量在指定面积内的密集情况。图2-2很直观地描述了这个测量单位。
图2-2
图2-2中从左至右同样3个1平方英寸单位面积的正方形面积中,谁的像素越多谁的像素密度就越高。
但是,你是否想过上面一直在谈论的像素究竟指的是什么?“呃,像素不就是在书写样式时使用的单位px吗?”其实不尽然。我们姑且把这一类像素称为CSS像素,留作下一节讨论。在谈论它们之前,我们先看看另一类像素——设备像素。
设备像素在英文中对应为device pixel或physical pixel,所以也可译为物理像素。无论是早期的CRT显示器还是如今的LCD显示器,现实的原理都是通过将一系列的矩形小点排列成一个大的矩形,让不同的小点呈现不同的颜色,最终来组成一幅完整的图像。例如,图2-3所示就是LCD显示器上一个4×4个设备像素排列成的矩阵。
图2-3
图2-3中的每一个“点”(dot)就是设备像素。在LCD显示器中,每一个设备像素又是由3个分别显示红绿蓝的子像素(subpixel)组成。LCD显示器的显示功能是通过调整每一个设备像素的子像素明暗来实现的,具体原理如图2-4所示。
图2-4
像素密度中所指的像素是设备像素,鉴于设备像素亦可称为物理点,所以PPI也可以称为DPI(dots per inch,每英寸点数)。但请注意这样的等价只有在描述显示设备的特征时才成立。在其他行业的上下文中两者含义并不同。
设备像素密度的计算方式正如它英文单词定义的一样所见即所得:使用对角线上的设备像素值,除以对角线的英寸长度,即为像素密度。图2-5为iPhone 5对应的计算像素密度的图解。
图2-5
我们当然希望像素密度越高越好(手机厂商也的确在往这个方向努力),因为像素密度越高意味着在有限的手机屏幕面积上能容纳的设备像素越多,能够展现更多的画面细节。同时因为肉眼几乎无法分辨物理像素点,设备看上去更加自然和平滑,原理如图2-6所示。
图2-6
但高像素密度同样也带来了副作用:单位面积内容纳的设备像素越多,也就意味着单个设备像素面积越小,如图2-7所示。
图2-7
可以预见的一种情况是,一个4×4像素组成的图片素材在标准像素密度(以下简称为标清)的设备(如普通的桌面显示器)上看上去有硬币大小,但是到了高像素密度(以下简称为高清)的设备上却只有指甲盖大小,如图2-8所示。
图2-8
反过来我们可以推论,如果想让高清设备与标清设备上的图片看上去同样大小,那么高清设备上的图片素材应该具有更多的像素,如图2-9所示。
图2-9
以一台23英寸的显示器为例,它的横向和纵向分别排列着1920×1080个设备像素,那么它的最高分辨率就可以达到1920×1080,我们称这个分辨率为原生分辨率(native resolution)或者物理分辨率(physics resolution)。
而屏幕只有5英寸的三星Galaxy S4的屏幕同样是由1920×1080个设备像素组成的。根据刚刚的结论,因为单个设备像素的面积过小,在普通显示器上可见的图片素材有可能此时在S4上几乎是很难分辨的。
手机厂商不可能没有留意到这个问题。为了设备的可用性,即图标和文字可以被正确识别和准确点击,在高清设备上的各类素材视觉上必须保证与标清设备同样大小。他们的解决方法很简单:如果素材在高清设备上显示过小,就把所有尺寸都放大一倍就好了(准确来说,Galaxy S4放大了9倍)。原来图片上的一个像素单位由一个设备像素单位显示,现在则由9个设备像素(3×3)单位显示,效果就是将图片做拉伸处理。如果网站提供的图片像素不够高,则会出现模糊情况,如图2-10所示。
图2-10
用iPhone 3GS和iPhone 4是最佳的对照实验,两者拥有相同的屏幕尺寸,但是iPhone 4的像素密度几乎是iPhone 3GS的2倍,像素是后者的4倍。但是两者屏幕上应用图标视觉上大小却是一模一样的,因为后者系统将所有的元素进行了4倍的放大(长2倍×宽2倍)。不过按照常识来说,将位图放大4倍务必会造成图片模糊。例如,下面这个例子为同一张图片在高清(左,模糊)和标清(右,清晰)设备上的对比,如图2-11所示。
图2-11
但感官上iPhone 4画面(如首屏图标)不仅没有模糊素材,反而看上去更细腻,是因为iPhone 4素材包含的像素数量是前者的4倍,尺寸也是前者的4倍,而设备像素足够小,能将细节全部展现出来。这同样也是Retina工作的原理。
在高清设备中,为了解决设备像素过小的问题,系统分辨率下每个像素会等于多个设备像素,而这个比值称为设备像素比(Device Pixel Ratio,DPR)。
从另一个方面来说,iPhone 3GS和iPhone 4都保持了相同的系统分辨率——480×320,但是iPhone 4的设备像素达到960×640,每一个系统分辨率下的像素由2个设备像素组成。这样就能容纳更多的细节。
请再次注意,放大素材的前提是被放大的素材最好有足够的尺寸和像素,否则多余的像素只能由系统计算出来而导致看上去模糊。这也是高清设备常常被诟病的地方。
上一节讲的像素密度和Web开发有什么关系?在我们编写样式代码时,常会用到另一个像素单位px。为了和设备像素区分开,我们把它称为CSS像素。如果说设备像素给我们的印象是机械的、固定的、物理的,那么CSS像素将会是灵活的、虚拟的、相对的。
为什么说它是相对的?
假设我在页面上画一个300 px宽度的块级元素。一般情况下,块级元素只相当于页面的部分宽度。如果使用浏览器的页面放大功能,10倍地放大页面,很快块级元素就会充满整个页面。但吊诡的是,此时我们既没有改变浏览器的宽度,也没有改变容器的样式宽度,那么浏览器为我们做了什么呢?它把每一个CSS像素的面积放大了,如图2-12所示。
图2-12
CSS像素默认与系统分辨率下像素大小相等。那么,在标清设备中,一个CSS像素应该是与一个设备像素大小相等。但是,在高清设备或者用户缩放的过程中,一个CSS像素也可以大于或等于多个设备像素,如图2-13所示。
图2-13
有关高清设备被诟病的问题,还有一个问题未被提及:假设在原生应用(注意,不是Web)的开发中,如果必须以设备像素为单位进行开发,那会是非常痛苦的一件事。以iPhone 3GS为参照,在3GS中,一个系统分辨率像素等于1个设备像素;在iPhone 4中,一个系统分辨率像素等于2个设备像素;在Galaxy S4中,一个系统分辨率像素等于3个设备像素;在iPhone 6 Plus中,一个系统分辨率像素等于2.46个设备像素。那么,如果一个按钮在iPhone 3GS中大小为100×100设备像素,在iPhone 4中大小就应该是200×200设备像素,在Galaxy S4中就应该是300×300设备像素,在iPhone 6 Plus中就应该是246×246设备像素。但是,我们实在无法为每一台设备根据它的设备像素准备如此多的素材。
我们希望有这么一种抽象的单位,只需告知手机它这个按钮或者素材占用几个这样的“抽象单位”,在显示时它就能自动缩放至合适的具体设备像素值。例如,iOS系统中的PT就是这样一个单位,当我们告诉它按钮占用的宽度是100×100 PT时,在iPhone 3GS中,它就意味着占用100×100的设备像素;在iPhone 4中,它就占用200×200设备像素。也就是说,系统会根据给出的PT值,再根据系统分辨率像素与设备像素的比值,换算出目标应该占用的大小。
上面所说的这种抽象单位称为与设备无关像素(device independent pixel)。
同理,CSS像素也是与设备无关像素。我们不用关心在不同设备上一个CSS像素会匹配多少个设备像素,浏览器会根据DPR为我们适配,在CSS基础上根据DPR做适当放大或者拉伸。但问题是,因为字体是矢量的关系,被放大后仍然足够清晰。而身为标量的图片则会变得模糊,因为拉伸状态下一个CSS像素需要横跨多个设备设备像素,每张图片上的单个像素信息仍然要被多个设备像素瓜分显示,自然就变得模糊起来。图2-12中对比的是同样CSS尺寸在高清和标清下的效果,很明显左侧的高清设备会显得更模糊。
为了解决高清设备中图片素材会变模糊的问题,需要为高清设备提供更大尺寸、细节更丰富的图片。此外,高清图片可以向下兼容,我们可以用CSS像素控制高清图片在标清设备上的大小,这样一张图片就走遍天下了,解决了响应式图片中的适配问题。可新的问题又出现了。
(1) 如何区分出高清设备和标清设备呢?如何为不同的设备提供不同的样式呢?
(2)如果用户在网络信息较差的手机上访问网站我们仍然提供高清图片,这是否有失偏颇?能否做到为不同的设备,甚至为不同的网络环境,提供不同的图片素材?
这两个问题留到第5章解决。
在桌面浏览器中,假设某个页面的<html>
宽度设置为自适应的100%
: html {width: 100%;}
,这意味着html
宽度始终与浏览器宽度保持一致。
同时,浏览器宽度也等价于浏览器可视区域宽度,所以在桌面浏览器中,浏览器可视区域大小决定了页面的布局。所见即所得,浏览器窗口多大,就会以多大的尺寸影响页面布局。我们称这里的可视区域大小为布局视口(layout viewport),或者简称为视口(viewport),如图2-14所示。
图2-14
在本书后面的内容中,如果没有特别说明,所称视口皆为此概念。当然,如果页面布局为固定宽度布局,页面布局就不会受到视口大小影响,但是在视口之外的内容,需要通过控制浏览器视口的滚动栏才能看见。
桌面端视口的特点是,浏览器区域大小受限于显示器屏幕大小,这也意味着页面的宽度不会超过浏览器屏幕宽度。读者可能会纳闷,为什么上面这么显而易见的事情会称为“特点”?因为在移动设备上不是这样的,也无法做这么一个设定。
假设在100%宽度的body
内还有一些占用宽度为10%的元素,如果以桌面视口的定义,10%宽度的元素实际宽度最大只能是系统分辨率的10%,则在1920×1080的显示器上浏览器最大化的情况下,该元素最大宽度为192 px。若在iPhone 4上浏览这个页面,如果iPhone 4仍然继承的是桌面视口的设定,那么用户看到只是一个32 px的元素,这是根本是无法识别的,这样的容器也不可用。
所以,移动设备的视口定义与桌面并不相同,但视口同样是用于控制页面布局渲染的。
对于移动设备上的浏览器来说,仍然需要一个区域用于控制页面的布局渲染。只不过这个区域不再以屏幕尺寸作为限制。
以iPhone 4为例,Safari渲染页面布局的默认宽度为980 px(CSS像素)。但是,用户可能觉得并非如此,因为当用户用Safari打开一个网站桌面版后,他看到的页面宽度刚好是与屏幕宽度相等的,如图2-15所示。但是iPhone 4的系统宽度分辨率不是320 px吗?
图2-15
其实之所以这样是因为浏览器做了两件事,如图2-16所示:
(1) 用980 px像素宽度渲染页面;
(2)将页面缩放至宽度与系统宽度一致。
图2-16
用户使用手势缩放页面也是同样的原理。浏览器和用户在这里并没有改变页面(准确来说是视口)的大小(size),只是改变了视口的缩放比例(scale)。
但是,浏览器使用默认的980 px去渲染页面并非是万能的。例如,当页面比较窄时(如只有320 px宽),页面效果会非常糟糕,如图2-17所示。
图2-17
设计人员希望以页面的宽度来渲染页面,并能自然自适应到系统宽度。于是手机厂商(最早是在Safari中)提供了一个名为viewport
的<meta>
标签设置视口大小。例如,在上一个用例中,当我们想以320 CSS像素渲染页面时,可以在<head>
标签中加入meta
标签,并设置如下:
<meat name="viewport" content="width=320" />
通过在content
属性中设置width
参数,即可以手动调整宽度,也可以添加initial-scale
参数来控制渲染时缩放视口的比例。如果未添加该参数,浏览器自动会将页面缩放至与浏览器宽度一致。
<meat name="viewport" content="width=320, initial-scale=1.0" />
以图2-18中320 px宽的图片为例,通过混合配置width
和scale
参数,我们能随意控制缩放比例页面的大小。
图2-18
在前几节中常说的“系统分辨率”就是视口大小,有兴趣的读者可以去viewportsizes.com上查找自己感兴趣的设备的视口大小。
大部分情况下我们都希望以系统分辨率的宽度来渲染页面,以尽可能地避免缩放,以及正确地响应设备(例如,页面布局是在320 px宽度的限制下进行设计的,我们当然希望设备以320 px宽度来渲染布局,而不是用980 px渲染后再进行缩放)。问题是不同设备的系统分辨率是不一致的,即使在同一种设备上,横竖两种持握方式也会让渲染方式不同。
但是,我们可以不用关心具体的数值,只要告诉浏览器:“无论什么设备,什么样的持握方式,请按照系统分辨率宽度渲染。”
于是我们可以将width
的值设置为device-width
。例如:
<meta content="width=device-width, initial-scale=1.0" />
那么我们就把获取具体系统分辨率宽度这个任务交给浏览器了,就由浏览器具体情况具体执行,如图2-19所示。
图2-19
本章的开头首先了解了关于屏幕PPI的定义,并引出了设备像素这个概念。虽然像素密度越高,能够表现的图像越细腻。但是,这一特性也需要开发者提供更高清的素材来支持,否则素材会显得模糊不堪。
随后提出了CSS像素的这个概念,并且梳理了CSS像素与设备像素之间的异同。通过CSS像素,能部分解决PPI在Web开发中的缺陷。
最后我们介绍了桌面端浏览器与移动设备浏览器在渲染页面时的差异,由此引出了视口(viewport)的概念,并学习了在移动浏览器上如何手动控制视口。
本书接下来的几章内容都与这些概念有关。例如,针对不同PPI设备,讨论如何为不同设备提供不同清晰度的图片;又如,示范如何获取视口的宽度,使用什么样的策略为不同视口设备提供不同样式的布局。