PWA开发实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-31 04:08:52

点击下载

作者:(以) 塔勒·爱特尔(Tal Ater)

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

PWA开发实战

PWA开发实战试读:

前言

渐进式 Web 应用(progressive Web app,PWA)是现代 Web 应用的一种激动人心的新形式。它利用了最新的 Web 功能,结合了原生移动应用的独特特性与 Web 的优点,为用户带来了新的体验。

本书将帮助你通过动手实践透彻地理解现代渐进式 Web 应用开发。

你将学习如何利用曾经专属于原生应用的特性构建 Web 应用。你将能够通过推送通知联系用户,在用户的主屏幕上占领先机,显著地加快网站速度,并且无论用户的网络连接状况如何,都能为其提供一个全功能的应用。

本书采用动手实践的学习方式,将一个现有网站逐章转化成现代渐进式 Web 应用。读者对象

本书主要是为开发人员准备的。如果你想利用已有的 Web 开发技能,学习如何构建现代渐进式 Web 应用,那么本书就是为你而写的。

本书假定你至少对使用 HTML 和 JavaScript 进行 Web 开发有基本的了解,但不要求你熟悉 JavaScript 相对较新的特性,比如 ECMAScript 2015、promise,以及 ECMAScript 2017 的异步函数。如果你已经了解这些现代语言结构,完全可以跳过或快速浏览讲解这些内容的注释。

本书可以帮助非技术人员熟悉并基本了解现代渐进式 Web 应用的功能。其中很多章都包含了案例研究,这些案例是通过对世界上最有影响力的一些网站背后的团队进行访谈得到的,包括 Twitter、《华盛顿邮报》、Housing.com 和 Lyft。无论你是管理人员、设计师、产品经理,还是任何参与原生或 Web 应用决策的其他人员,了解如今可能实现的技术,将有助于你更有效地开展工作。本书内容

哥谭帝国酒店是本书虚构的酒店,它的网站很简单。阅读本书时,你将接管这个网站,通过 service worker 技术对它进行增强,使其几乎可以瞬间加载(哪怕是在最慢的连接状况下),并确保所有的功能都可以在用户完全离线时使用(包括查看用户的预订,甚至是发起新预订)。你将学习如何让用户添加一个图标到手机的主屏幕上,并从此处启动你的渐进式 Web 应用。最后,为了实现原生应用般的体验,你将添加推送通知,如此一来,就算用户离开你的网站,你也能联系并重新召回他们。

本书还探讨了开发渐进式 Web 应用时需要重点考虑的一些因素,并专注于帮助你切实理解这些概念,进而成为更高效的开发人员。此外,本书还介绍了实用的开发工具、安全因素,以及 service worker 的生命周期。

虽然本书大部分章节都集中在通过动手实践进行学习上,但其中两章(第 5 章和第 11 章)会让你思考渐进式 Web 应用提供的新功能,让你意识到它们不仅仅是应用于你的应用的一套新技巧。

第 5 章探讨了离线优先的 Web 应用原则,以这种方式构建的现代 Web 应用不会把失去网络连接视为错误,而是能够为之做好计划并优雅地处理。

第 11 章探讨了渐进式 Web 应用为用户界面带来的一些新挑战和新机会。渐进式 Web 应用改变了用户对 Web 应用的期望。其中一些挑战包括:增强用户的信任,让他们相信离线时自己的数据不会丢失;告诉用户离线时看到的内容可能是几小时之前的,并且让用户知道无论发生了什么重要的变化,应用会发送通知给他。若应对得当,这些挑战也是大好时机,可以借此增强用户对应用的信任,提高转化率,并在用户的手机上取得永久性位置。

本书最后介绍了一些即将到来的技术和浏览器 API,它们将进一步推动渐进式 Web 应用的发展。排版约定

本书采用以下排版约定。● 黑体表示新术语或者重点强调的内容。● 等宽字体(constant width)表示程序片段,以及正文中出现的

变量、函数名、数据库、数据类型、环境变量、语句和关键字等。● 等宽粗体(constant width bold)表示应该由用户输入的命令或

其他文本。● 等宽斜体(constant width italic)表示应该由用户替换或取决于

上下文的值。 该图标表示渐进式 Web 应用的案例研究。

 该图标表示一般说明。 该图标表示从另一个角度看待同一问题。 该图标表示警告或提醒。代码示例

补充材料(包括示例代码、练习题等)可以从 https://github.com/TalAter/gotham_imperial_hotel 下载。

本书旨在帮助你做好工作。一般来说,你可以在程序和文档中使用本书的代码。除非你使用了很大一部分代码,否则无须联系我们获得许可。例如,使用本书的几段代码编写一个程序不需要获得许可,销售和分发 O'Reilly 书中示例的光盘则需要获得许可。引用书中示例代码来回答问题无须获得许可,把书中的大量示例代码放入你的产品文档则需要获得许可。

我们很希望但并不强制要求你在引用本书内容时加上引用说明。引用说明一般包括书名、作者、出版社和 ISBN。例如:“Building Progressive Web Apps by Tal Ater (O'Reilly). Copyright 2017 Tal Ater, 978-149-196165-0”。

如果你觉得自己对示例代码的使用超出了合理引用或者上述许可的范围,请随时通过 permissions@oreilly.com 联系我们。O'Reilly Safari

Safari(之前称作 Safari Books Online)一个针对企业、政府、教育者和个人的会员制培训和参考平台。

会员可以访问来自 250 多家出版商的上千种图书、培训视频、学习路径、互动式教程和精选播放列表,这些出版商包括 O'Reilly Media、Harvard Business Review、Prentice Hall Professional、Addison-Wesley Professional、Microsoft Press、Sams、Que、Peachpit Press、Adobe、Focal Press、Cisco Press、John Wiley & Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、Adobe Press、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones & Bartlett、Course Technology 等。

要了解更多信息,可以访问 http://www.oreilly.com/safari。联系我们

请把对本书的评价和问题发给出版社。美国:

  O'Reilly Media, Inc.

  1005 Gravenstein Highway North

  Sebastopol, CA 95472

中国:

  北京市西城区西直门南大街 2 号成铭大厦 C 座 807 室(100035)

  奥莱利技术咨询(北京)有限公司

O'Reilly 的每一本书都有专属页面,你可以在那里找到本书的相1关信息,包括勘误表、示例以及其他信息。 本书的网页地址是:http://shop.oreilly.com/product/0636920052067.do本书中文版的勘误请到 http://www.ituring.com.cn/book/2040 1查看和提交。——编者注

对于本书的评论和技术性问题,请发送电子邮件到:bookquestions@oreilly.com

要了解更多 O'Reilly 图书、培训课程、会议和新闻的信息,请访问以下网站:http://www.oreilly.com

我们在 Facebook 的地址如下:http://facebook.com/oreilly

请关注我们的 Twitter 动态:http://twitter.com/oreillymedia

我们的 YouTube 视频地址如下:http://www.youtube.com/oreillymedia致谢

我首先要感谢 Alex Russell 和 Jake Archibald。在 2014 年的谷歌 I/O 开发者大会上,我偶然听到了他们的演讲。在那之前,我甚至不知道本书中涵盖的任何内容意味着什么。他们的演讲和本书的写作帮助我增加了了解。

我还要感谢在现实世界中构建渐进式 Web 应用的优秀人才,特别是那些在本书中分享经验的人。感谢 Joey Marburger、Ritesh Kumar、Rahul Yadav、Nicolas Gallagher、Chris Nguyen 和 Jeremy Toeman。

感谢 O'Reilly 团队帮助我出版了本书,包括 Ally MacDonald、Jeff Bleiel、Sonia Saruba、Colleen Cole、David Futato、Rebecca Demarest、Heather Scherer、Ellen Troutman、Amanda Kersey,以及 Karen Montgomery 和她的戴胜鸟(本书封面上的动物)。

还要感谢谷歌、Opera、Mozilla 和微软的开发团队,在他们的帮助下,本书和本书中介绍的技术才成为了现实。感谢优秀的开发者们帮助我更好地理解了这项技术。感谢 Jeffrey Posnick、Addy Osmani、Matt Gaunt 和 Paul Kinlan。

感谢 Alex Feyerke 和 Hoodie 团队,他们围绕离线优先做出了开创性的工作,并通过写作记录下来。本书中的不少内容都是受到了他们的启发。

编写这样一本书最困难的一点,是在一年多的时间里几乎没有收到外部反馈,这带来了很大的不确定性。我要感谢所有在本书写作过程中抽出时间向我提供反馈意见的人。感谢 James Stanley、Patrick Conant、Fabio Rotondo、Neville Franks 和 Florian Semrau。

最后,我要感谢本书的技术审阅者,他们对本书进行了全面的技术审查,以保障我所写的内容确实有意义。感谢 Andreas Bovens、Kenneth Rohde Christiansen、Patrick Kettner 和 Thomas Steiner。第1章渐进式Web应用介绍词是被遗忘的名之浅影。因为名有力量,所以词也有力量。词可以点燃人们心中的火焰,也可以让最狠的心流下眼泪。七个词便可以让一个人爱上你,十个词便可以让最坚强的人顿失意志。但是词不过是火之描绘,而名才是火之本身。——Patrick Rothfuss,《风之名》

每隔几年,Web 就会经历一个关键性时刻。在这个时刻,几种独立的技术相互碰撞,在公众中引起轰动。这些技术可能已经诞生多年,也可能是刚刚获得浏览器支持的新技术。但对于旁观者来说,似乎在一个闪光的瞬间,Web 突然向前跃进了一步。

Ajax 技术就是这样。它似乎是某一天不知从何处突然蹦出来的(尽管很多底层技术已经存在多年,如 XMLHttpRequest),改变了我们对 Web 的理解:一系列相互链接的、大部分是静态的页面。

Ajax 本身只是 Web 2.0 革命的一部分,而 Web 2.0 是另一个强大的名字,它在 2004 年从天而降,一夜之间引爆了世界。

几年后,移动优先(mobile-first)出现了,它标志着我们对 Web 开发的看法发生了转变。通过给一套设计原则命名,这两个单词获得了难以置信的力量。只用这两个单词,我们就可以大声说,用户坐在台式机前,用 20 英寸的显示器和一根连接到墙上的电缆上网的时代已经结束了。这两个单词让我们明白,是时候改变 Web 开发的方式了。

这样的时刻往往不是在技术诞生的时候出现的,而是在它被命名的时候。

名字就是有这样的力量:它让我们掌握新思想、讨论新概念,提醒我们注意在表面下酝酿的风暴。1

一个同样巨大的转变正在发生。幸运的是,它有一个名字。更确切地说,幸运的是,Alex Russel 和 Frances Berriman 1有一天共进晚餐,并想出了一个名字。详细内容请参见 Alex Russel 的博客文章:“Progressive Web Apps: Escaping Tabs Without Losing Our Soul”。1.1 Web反击战

渐进式 Web 应用是一种崭新的 Web 应用,它结合了原生应用的优点和 Web 少冲突的特点。

渐进式 Web 应用始于简单的网站,但随着用户的使用,它不断获得新的权限,并从网站变成一种更像传统原生应用的形式。

想象一下,你早上醒来,抓起手机,浏览本地列车公司的网站。你快速查看了上班需要乘坐的列车的时间表,然后关闭浏览器,并把手机放进口袋。下班时,你再次访问该网站,查看下一趟列车何时发车(你甚至没有注意到正在乘坐的电梯没有手机信号,因为列车公司的网站依然在运行,即使你处于离线状态)。次日,当你再次访问该网站时,浏览器询问你是否要添加快捷方式到主屏幕上,你欣然同意。当天晚些时候,当你从主屏幕的图标登录该网站时,它通知你,由于施工,列车可能会延误,并问你是否想接收关于列车时刻变化的后续通知。第三天早上,当你醒来时,手机收到消息推送,说该列车会延误 15 分钟。你按下闹钟上的延时按钮,又多睡了一会。

渐进式 Web 应用从一个简单的网站开始,慢慢获得新的权限,直到和原生应用一样,而不是试图把你送到应用商店,希望你安装应用。就这样,列车公司在你的手机上一步一步赢得了永久性的位置。

这种新的渐进增强模型,取代了只提供“安装”和“不安装”两种选择的原生应用。渐进式 Web 应用能赢得用户的信任,并按需获得新的权限。

你可能会问,为什么这是对原生应用的改进呢?我们为什么不坚持使用原生应用呢?好吧,除非你是少数的幸运儿,否则你应该知道,原生应用已经行不通了。用户安装应用的可能性在逐年减小,获取新用户的成本在逐渐增长,留住用户变得越来越困难。1.2 当前的移动领域

当第一款 iPhone 在 2007 年推出时,它的杀手级功能是让你能够在手机上浏览网站。当移动应用于一年后诞生时,开发人员终于能够突破网页的功能限制了(同时,由于应用商店的引入,也面临许多新的限制)。

Web 具有高级图像技术、地理位置识别、消息推送、离线可用性、主屏幕图标等特性,但在许多开发人员的眼中,Web 似乎相形见绌。原生应用接管了全球(和我们的手机)。

但这种趋势正在转变。虽然我们在手机和移动应用上花费的时间比以往任何时候都多,但我们使用的应用却越来越少。用户安装的应用少了,而且只使用其中几个。如果你的应用在应用商店中排在前 10 位,你可能不会有这种困扰。但是,现在尝试将一款新的应用打入市场几乎是不可能的,成本就更别提了。 移动用户的行为根据 2016 年 comScore 的报告,在移动设备上,平均每人将 84% 的时间花在最流行的 5 个应用上。抱歉,这些不是你的应用。在平板电脑上,这个比例甚至更高,用户将 95% 的时间花在了这 5 个应用上。该报告还表明,移动网站比原生应用更容易获得大量用户。拥有 500 万以上访问者的移动网站接近 600 个,比拥有类似受众的原生应用多 4.5 倍。排名前 1000 位的移动网站拥有的用户数量几乎是排名前 1000 位的原生应用的 3 倍,而且前者的用户增长速度是后者的两倍。

让用户安装并使用应用,如同在一个漏斗形空间里挣扎求生。用户需要知道你的应用(通过传统的在线广告或你的网站),然后必须访问其在应用商店中的页面,接下来需要点击安装。他们需要同意授予应用不同的权限,然后等待应用下载并安装。最后,他们至少要启动一次应用,甚至使用它。

当用户安装他们知道并喜欢的应用(比如 Twitter 或者 Facebook)时,这个漏斗看起来并不算太糟糕。但是大量研究表明,在这个漏斗的每一个环节,平均有 20% 的用户丢失了。应用开发人员为广告点击付费后,发现只有不到 20% 的用户实际启动了应用,这种情况并不罕见。

网站竭尽所能地让你安装他们的应用,甚至采用了一种新的广告方式。你肯定见过这种广告:当你打开一个网站,想看一篇短文或者查询明天的天气时,发现所需信息近在眼前,但是一个横幅广告随即弹了出来,挡住了你想看的内容。它问你是否愿意安装一个应用,而不直接阅读已经在你眼前的内容。

有些人称之为全页间隙式广告(full-page interstitial ad)。我喜欢一个较短的名字:用力关门(the door slam,如图 1-1 所示)。要详细了解全页间隙式广告的作用与副作用,请参见附录 B。图 1-1:一种常见的“用力关门”式广告

让用户在手机上安装你的应用是一场残酷、昂贵的战斗。然而,原生应用相较于 Web 的优势,让应用开发者心甘情愿为每一次应用安装忍受痛苦。

与传统网站不同,原生应用的生命并不限于用户首次发现它到离开这段短暂的时间(有时候,这段时间只有几秒钟)。一旦安装,原生应用就在你的主屏幕上占据了永久性的位置。它可以随时通过消息推送提醒你它的存在。这让开发者有很多机会在应用的长生命周期里尝试获得投资回报。

但随着渐进式 Web 应用的推出,潮流终于开始转向了。这些超能力所带来的优势曾经是原生应用专有的,但现在可以移植到 Web 应用上了。将这些能力与 Web 少冲突、一步漏斗的特点(使用点击链接取代安装应用)结合起来,你就会明白为什么用户、Web 开发者和企业都能因拥抱渐进式 Web 应用而获益良多。1.3 渐进式Web应用的优势

随着上述超能力的引入,渐进式 Web 应用实现了我们寄予原生应用的很多期望。以下是本书中将介绍的部分优势。

无连接状态下的可用性

和传统网站不同,渐进式 Web 应用不依赖于用户的连接状态。当用户访问一个渐进式 Web 应用时,它会注册一个 service worker(参见 1.4 节),service worker 可以检测并响应用户连接状态的变化。无论用户是离线、在线,还是处于网络不稳定状态,它都可以提供完整的用户体验。

用户可以在穿越大西洋的航班上使用你的渐进式 Web 应用,甚至可以进行操作(例如发布信息、回复事件或者评论文章),用户知道他的操作会在网络恢复之后立刻完成,即便他关闭了应用和浏览器。详情请参见第 7 章。

渐进式 Web 应用引入了一定程度的可靠性,将用户的信任程度提升到原本只有原生应用才能达到的程度。用户知道他可以在任何时间打开 WhatsApp 应用,写一条短信,然后关掉手机,而无须担心连接状态。到目前为止,Web 网站还没获得这种信任,这也是用户更偏向于使用原生应用的原因之一。

加载速度快

使用 service worker,我们可以创建一个瞬间运行的网站,无论用户的网速极快,还是使用不可靠的 2G 连接,甚至是在完全没有网络连接的状态下。网站可以在几毫秒内加载,这比过去的 Web 要快得多,甚至比原生应用还要快。在第 5 章中,我们将学习如何实现这一点,并探讨离线优先的原则。

推送通知

渐进式 Web 应用可以向用户推送通知(甚至是在用户离开网站几天后)。这些通知提供了一个好机会,让你得以重新吸引用户,并提醒他们回到应用。渐进式 Web 应用的通知是完全原生化的,和原生应用的通知没有区别。参见第 10 章了解更多关于推送通知的内容。

主屏幕快捷方式

一旦用户表现出对渐进式 Web 应用感兴趣,浏览器就会自动建议用户添加快捷方式到主屏幕上——和原生应用完全一致(如图 1-2 所示)。参见第 9 章,了解如何在用户的主屏幕上占据位置。图 1-2:应用安装横条

媲美原生

渐进式 Web应用从主屏幕启动的过程可以完全原生化,和原生应用相似。前者在加载过程中可以显示启动画面;可以以全屏模式运行,摆脱浏览器和手机系统的 UI 界面;甚至可以锁定屏幕方向(这对于游戏应用而言至关重要)。

参见第 9 章了解更多细节。 Lyft——平台越多,乘客越多除了用户体验方面的好处之外,渐进式 Web 应用也为采用它的企业提供了额外的好处。Lyft 提供流行的打车服务,这家公司的收入完全依赖移动应用。在不断努力获得更多用户的过程中,Lyft 公司发现自己不得不支持越来越多的设备和移动端操作系统版本。随着应用的演进,Lyft 不得不放弃支持旧版本的 iOS 和安卓系统,否则维护成本将不断增长。Lyft 没有放弃这些潜在的客户(占据了大约 8% 的 iOS 用户和 3% 的安卓用户),而是构建了一个渐进式 Web 应用。采用了渐进式 Web 应用之后,Lyft 团队成功减少了支持多应用和多设备的技术和运营成本。更重要的是,他们不仅获取到了使用 iOS 和安卓系统的新用户,还兼顾了他们以前忽略的 Windows Mobile 和 Amazon Fire 用户。1.4 浏览器标签页、Web和service worker

任何渐进式 Web 应用的核心都是 service worker 技术。

service worker 体现了我们对 Web 开发看法的转变。我们花几分钟了解一下这项技术的定位,这对于理解它的潜力至关重要。

在 service worker 出现之前,我们的代码只能在服务器或者浏览器端运行,而 service worker 的出现引入了另外一层。

service worker 是一种脚本,可以通过注册它来控制你站点中的一个或多个页面。一旦安装完毕,service worker 就会独立存在,而不是属于某个浏览器窗口或者标签页。

service worker 可以监听并响应在其控制之下的所有页面的事件。向 Web 请求文件等事件,可以被它拦截、修改、传递并返回给页面(参见图 1-3)。图 1-3:浏览器标签页、Web 和 service worker 的关系

这意味着页面和 Web 之间增加了一层,它可以响应请求,而无论网络连接状态如何。service worker 层甚至可以在用户离线的情况下正常工作。它可以检测到离线状态或者服务器响应慢的情况,并返回缓存内容取而代之(参见图 1-4)。图 1-4:用户离线时,页面与 service worker 进行通信

再进一步,这意味着即使用户关闭浏览器中运行着你的应用的所有标签,service worker 层依然可以和服务器通信(参见图 1-5)。它可以接收并显示推送通知,或者确保任何用户操作都能够被传递到服务器(即使用户一边走进电梯一边进行操作,并在重新建立网络连接前关闭了应用)。图 1-5:在用户离开页面后,service worker 与服务器进行通信

现在你知道为什么 service worker 是每一款渐进式 Web 应用的核心了。它的持久性让渐进式 Web 应用能够实现我们对于一款应用的期望。它弥补了 Web 应用的缺失环节,在过去只有原生应用能够做到的事情,现在渐进式 Web 应用也能做到了。

但也许 service worker 最大的优势在于它就是简简单单的 JavaScript 文件。它的写法和其他 JavaScript 代码一样,前端开发者没有额外的语言学习成本。

对于开发者来说,若能理解 service worker 和本书所涉及的其他相关技术,收益将是巨大的。这些技术让我们可以利用现有的 Web 技术,包括 JavaScript、HTML 和 CSS,编写出完全可以媲美甚至超越原生移动应用的 Web 应用。第2章你的第一个service worker2.1 设置示例项目

本书将通过动手实践的方式学习渐进式 Web 应用。

从本章开始,我们将接管一个简单的 Web 应用(属于本书虚构的哥谭帝国酒店),并逐章对其进行改善。每一章都会在前一章的基础上进行提升,并且在每章的最后,你都可以得到一个可用的 Web 应用。

到本书结束时,你将把这个简单的网站变成一个功能全面的渐进式 Web 应用。

为了按照代码示例进行学习并亲自动手尝试,你可以把应用的源代码复制到你的本地机器上。你可以在哥谭帝国酒店的 GitHub 仓库(https://github.com/TalAter/gotham_imperial_hotel/issues)中获取源代码。

请注意,为了复制代码并在本地运行,你需要能够在本地机器中运行 Git、Node.js 和 NPM。否则,你就需要通过其他的方式进行本书的学习(例如从 GitHub 直接下载源代码,并在远程服务器中运行),我并不推荐这样做。

首先,打开你电脑中的命令提示符(控制台),切换到你希望下载源代码的目录,并运行以下命令:git clone -b ch02-start git@github.com:TalAter/gotham_imperial_hotel.gitcd gotham_imperial_hotelnpm install

上述命令会复制哥谭帝国酒店的 Web 应用的源代码,把分支切换到 ch02-start,并安装运行代码所需要的依赖库。

接下来,你可以使用下列命令来启动一个本地服务器,用浏览器打开你的站点:npm start

现在如果你在浏览器中打开 http://localhost:8443/,应该能够看到哥谭帝国酒店的 Web 应用了。 如果浏览器中没有成功加载 Web 应用,请确认以下内容。● 你已经安装 Git、Node.js 和 NPM,并且可以在命令行(例如 macOS 中的 Terminal 或者 iTerm,Windows 的命令提示符或者 Cygwin)中使用它们。● 你已经遵循了前面所有的步骤。如果你现在依然不能运行应用,请随时到我们的 GitHub 问题跟踪中寻求帮助。

现在,你可以在自己喜欢的 IDE 或者编辑器中打开该项目,并跟随本书将该站点转换成一个渐进式 Web 应用。

由于每一章的代码都建立在前几章所做的更改之上,所以在每一章开始前,你的代码都需要包含之前所有的更改。如果你打算跳过本书中的任何编码练习,或者整章跳过,则可以在命令行中运行以下两个命令,将代码切换到每章开头的状态:git reset --hardgit checkout ch04-start

上述命令会重置所有本地已完成的修改,并把分支切换到那一章开始之前的状态。请确保将第二条命令中的分支名称更改成当前所在章节的名称。例如,当你开始阅读第 6 章的时候,只需运行 git checkout ch06-start,就可以切换到包含前面五章完成的所有更改的分支中。2.2 欢迎来到哥谭帝国酒店

本书将通过虚拟的哥谭帝国酒店的网站这一项目来探索渐进式 Web 应用。

这个简单的网站包含两部分:

(1) 首页,包含了酒店介绍、地图、最近活动列表,以及一个发起新预订的表单;

(2) 个人账号页面,包含了用户的预订列表、最近活动,以及一个发起新预订的表单。

虽然看起来简单,但是这两个页面已经包含了构成重内容网站以及类似于原生应用的 Web 应用的大部分元素。

通过学习本书,你就可以把这个简单的网站变成一个功能齐全的渐进式 Web 应用。 挑战不同,实现方式各异在探索渐进式 Web 应用的不同特性时,我们偶尔会从哥谭帝国酒店应用中后退一步,到一个不同的场景中探索相同的想法。虽然我们的酒店应用更类似于一个传统的商务网站,但这些注解将从一个更类似于传统原生应用的应用的角度探索相似的挑战。通过探索这些方法的异同,我们可以更好地理解每项特性是如何适应到不同的项目中的,以及不同的企业如何从每项新特性中获益。msger 是我们将在这些注解中探索的一个虚构的消息应用,这款应用允许用户发送 140 个字符的短消息,并能展示最新的用户消息流。当新消息出现的时候,它会被添加到消息流的顶部,旧消息将移到列表下方(见图 2-1)。图 2-1:我们的示例消息应用2.3 熟悉代码

在开始之前,我们先来熟悉一下这个应用的基本代码结构。

项目主目录中包含了两个最重要的目录。

public

包含了网站的所有客户端代码,以及运行代码所需的其他文件,例如图片和样式表。

server

包含了运行网站、跟踪预订、发送通知等的服务端代码。

本书中的所有编码练习只涉及 public 目录,但是你可能需要时不时关注一下 server 目录,特别是在第 10 章中。 写在介绍代码之前如果你看看应用代码的初始状态,会发现这份代码非常简单。为了增强可读性以及清楚地展示我们将学习的关键原理,代码中有不少地方没有遵循最佳实践,甚至违背了常识。当你学完本书,将有机会大幅改进这份代码。希望你不仅学会了如何从头开始构建一个渐进式 Web 应用,还学会了如何改进现有的项目,将其转化为一个渐进式 Web 应用。本书中没有使用很多的现代 ES2015 语言结构,这样你就可以专注在本书的主题上,而不是那些你可能熟悉也可能陌生的新语法。要查看本书中的代码如何从 ES2015 中受益,请参阅附录 A。2.4 当前的离线体验

读完上一节后,你应该已经拥有一份哥谭帝国酒店 Web 应用的代码副本,以及一个可运行该应用的本地 Web 服务器。

要确保你当前的代码是处于本章开始时的状态,可以在命令行中输入下列命令:git reset --hardgit checkout ch02-start

接下来运行 npm start 命令,开启一个运行这个网站的本地 Web 服务器,然后在浏览器中打开它(http://localhost:8443/),你应该就能够看到这个网站的全貌了(如图 2-2 所示)。图 2-2:哥谭帝国酒店首页

如今的 Web 就如同这个网站,内容丰富、界面美观、功能实用。但事实上,这样的 Web 是你从开发者的角度看到的。作为开发者,我们经常会通过比较先进的台式机、笔记本电脑或者移动设备访问网站。我们有着可靠的连接,或者是连接到本地服务器,或者是连接到一个距离很近的开发服务器。但是,用户体验到的 Web 应用可能是完全不同的。试想,当用户在离线的时候访问我们的网站会如何呢(见图 2-3)?图 2-3:一位用户在电梯中访问了我们的示例 Web 应用

不幸的是,对于很多用户来说,这才是他们如今使用的 Web。终于,service worker 的出现让我们有机会处理这种情况了。 模拟离线状态在本书使用这个示例应用的过程中,我们经常需要模拟离线状态。由于离线状态的本质是用户无法访问你的服务器,因此,模拟这种状态的一种方法是关闭你的开发服务器。在运行着本地服务器的命令行中,按下 Ctrl+C 可以停止服务器进程。接下来,在浏览器中重新加载应用,就可以看到用户离线访问的效果了。当你想要重新“上线”的时候,只需再次运行 npm start 命令即可。这种基本的方法在开发过程中可以很好地模拟离线状态,但是一旦将代码发布到生产环境,每当你想进行某项测试时就“下线”生产服务器,这是行不通的。所幸,大部分现代浏览器都包含了用于模拟离线状态的工具,甚至可以模拟不同的连接速度(见图 2-4)。详情请参见 4.8 节。图 2-4:在 Google Chrome 中模拟离线状态2.5 创建你的第一个service worker

让我们来控制用户的离线体验。

首先从当前页注册一个新的 service worker。打开 js/app.js 文件,在文件开头添加如下代码:if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/serviceworker.js") .then(function(registration) { console.log("Service Worker registered with scope:", registration.scope); }).catch(function(err) { console.log("Service worker registration failed:", err); });}

代码首先验证了当前浏览器是否支持 service worker。然后通过调用 navigator.serviceWorker.register 方法注册了我们的 service worker,这个方法接收两个参数,第一个是我们的 service worker 脚本 URL,第二个是可选的选项对象(这里省略了这个参数,但会在 2.11 节探讨这个问题)。

通过在使用之前先测试 service worker 的支持情况,可确保不会将使用旧浏览器来访问应用的用户排除在外,并同时向使用现代浏览器的用户提供增强的体验。这种渐进增强式的实践是构建这个应用的核心(参见 2.6 节)。

上述的 register 调用会返回一个 promise。如果 promise 完成,就意味着 service worker 成功注册了,在 then 语句中定义的函数就会被调用。否则,如果 promise 遇到任何问题,就会执行 catch 块内定义的函数。

现在,如果你在浏览器中刷新示例应用,应该会在浏览器的控制台中看到一条错误信息,告诉你“Service worker registration failed.”。1如果你没有看到这条错误信息,请确保你的本地服务器正1在运行,并阅读关于浏览器支持的内容(即2.5 节中的“service worker 的浏览器支持度”。

service worker 注册失败了,promise 的状态也因此失败,因为目前我们还没有创建 serviceworker.js 文件。

创建一个空文件,命名为 serviceworker.js,并放置在项目根目录的 public 文件夹中,即 public/serviceworker.js。此时,如果你刷新浏览器,应该会看到一条消息,告诉你“Service worker registered with scope: http://localhost:8443/”。虽然现在我们的 service worker 只是一个空文件,但它仍然是一个有效的 service worker,并且已经注册成功了。 可能你会想把 serviceworker.js 文件移动到项目的 js 子目录。请暂时把它放在根目录中。在 2.11 节中,你将会了解到这一点的重要性。

让我们开始探索 service worker 可以做的事情。

在 serviceworker.js 文件中添加下列代码:self.addEventListener("fetch", function(event) { console.log("Fetch request for:", event.request.url);});

这段代码通过调用 self 变量的 addEventListener 方法(在 service worker 中,self 指向 service worker 本身),在我们的 service worker 中添加了一个事件监听器。这个监听器会监听所有经过 service worker 的 fetch 事件,并运行我们接下来定义的函数,将事件对象 event 作为唯一参数传递。在我们定义的函数中,通过访问事件的 request 对象(这是 fetch 事件中的一个属性),把这次请求的 URL 打印到控制台中。

现在刷新页面,应该能看到页面发起的所有请求都被记录在浏览器的控制台中。(如果你没有在控制台看到任何 URL,可能是因为旧版本的空 service worker 依然在控制页面。参见“service worker 生命周期”获取相关技巧。)service worker 生命周期你可能已经注意到,当你修改 service worker 文件的时候,这些修改并没有在刷新浏览器之后立即生效。这是因为旧版本的 service worker 依然处于激活(active)状态,与此同时,新的 service worker 仍然处于等待(waiting)状态,直到旧版本不再控制页面为止。虽然这看起来可能非常不方便,但它实际上是 service worker 的一项非常强大的特性。我们将在第 4 章更详细地探讨这一点。为了简化开发,你可以告诉浏览器让新的 service worker 立即控制页面。在 Chrome 浏览器中,这可以通过开发者工具的 Application 选项卡实现:在“Service Workers”选项中,勾选“Upload on reload”(见图 2-5)。这样可以确保每次修改 service worker 并刷新页面时,新的 service worker 会立即控制页面。图 2-5:打开“Upload on reload”

上述的演示可能没有给你留下深刻的印象,那么请思考:我们的页面发起的每一个请求(包括向第三方服务器发起的请求)现在都会经过我们的 service worker。现在所有的这些请求都可以被拦截、分析,甚至被操控。

让我们通过一个例子看看这个特性有多强大。

把 serviceworker.js 中的代码替换成下列代码,并刷新你的浏览器:self.addEventListener("fetch", function(event) { if (event.request.url.includes("bootstrap.min.css")) { event.respondWith( new Response( ".hotel-slogan {background: green!important;} nav {display:none}", { headers: { "Content-Type": "text/css" }} ) ); }});

上述代码监听 fetch 事件,并检查每个请求的 URL 是否包含 bootstrap.min.css 字符串。如果包含,service worker 就会动态创建一个 Response 对象,其中包含了自定义的 CSS,并使用这个对象作为响应,而不会向远程服务器请求这个文件(见图 2-6)。图 2-6:重写 CSS 请求,并修改背景颜色

仅仅用了短短几行 JavaScript 代码,我们就创建了一个可以拦截第三方服务器请求的 service worker,凭空编写了一个新的响应并展示给浏览器,看起来就像是服务端返回了结果一样。从本质上说,我们在浏览器内部创建了一个代理服务器。 service worker 浏览器的支持度虽然 service worker 的规范在 2014 年才发布,但是浏览器接纳 service worker 的速度却出人意料地快。到 2015 年底,Chrome、Opera、Firefox 和 Samsung Internet 都已经支持 service worker 了。在本书出版的时候,WebKit 团队正致力于将 service worker 融入到 iPhone 和所有基于 Safari 的浏览器上,另外 Microsoft Edge 团队也在努力当中。要了解 service worker 的浏览器支持的最新状态,以及相关技术,请参见 Jake Archibald 的网页“Is ServiceWorker Ready?”。2.6 什么是渐进增强

渐进增强(progressive enhancement)是我们的应用以及任何现代 Web 应用的核心理念。

渐进增强能够为用户提供尽可能多的功能体验。这意味着开发的网站不会仅仅因为用户浏览器不支持某项功能就崩溃。

可以把渐进增强看作一种分层构建 Web 应用的方式。从基础的内容、简单的 HTML 链接、图像等开始。然后为用户提供 JavaScript 支持,添加一层增强的链接用于异步获取内容,并使用交互式的谷歌地图替换地图的静态图片;为支持 service worker 的浏览器添加离线支持;向可以接收推送的用户发送通知。

这样做的优点是,不仅可以为所有用户提供功能完整的应用,还可以使你的网站兼容所有用户(包括那些使用旧浏览器或者功能电话的用户),而且还可以让搜索引擎正确地索引所有内容。

在注册 service worker 时,我们首先要验证浏览器支持情况。支持 service worker 的浏览器用户将会享受到增强的体验,而其他用户仍将获得过往的全部体验。我们在逐步增强应用的同时,不会影响到其他的用户。

注意不要混淆渐进增强和渐进式 Web 应用这两个术语。虽然理想的做法是使用渐进增强的方式来开发渐进式 Web 应用,但是从技术上来说这不是必须的。你可以开发一个这样的渐进式 Web 应用,它在现代浏览器上运行得很好,但是在其他所有浏览器中都很糟糕——请不要这样做。2.7 HTTPS和service worker

正如你刚才看到的,service worker 可以拦截请求、修改内容,甚至把内容完全替换成新的响应。为了保护用户和防止中间人攻击,避免恶意的第三方利用这些权限,只有使用安全连接(HTTPS)的页面才能注册 service worker。

在开发过程中,你可以通过主机名 localhost 使用 service worker,这样可以绕过安全连接的限制(例如,http://localhost/ 和 http://local-host:1234/user/index.html 都可以注册并使用 service worker)。但是一旦你把 Web 应用部署到服务端,就必须使用安全的 HTTPS 连接来保证 service worker 正常工作。

随着 Web 变得越发强大,它的许多新特性都要求使用 HTTPS。除了 service worker,其他很多新特性同样有这个要求。例如,SpeechRecognition 等其他 API 虽然没有强制要求使用 HTTPS,但是在 HTTPS 环境下,其功能要强大得多。甚至还有一些功能过去在非安全连接下可以使用,但是已经变成仅在 HTTPS 环境下可用了,例如 Geolocation API。

如果你还需要更多的动力以迁移到 HTTPS,Google 宣布它已开始在搜索排名中给予使用安全连接的网页略高的权重。

如今,网站使用 HTTPS 比以前更便宜,也更简单。很多新的证书机构甚至已经开始免费提供 SSL 证书,而且配置服务器的新工具使得配置过程变得更加简单。如果你仍然坚持使用 HTTP,很快就会没有借口了。2.8 从Web获取内容

在前面编写的代码中,我们通过指定内容和头部,从零开始创建了一个新的响应对象,并使用它来响应请求。

而 service worker 更广泛的用途是响应来源于网络的请求。

把 serviceworker.js 的代码替换成下列内容:self.addEventListener("fetch", function(event) { if (event.request.url.includes("/img/logo.png")) { event.respondWith( fetch("/img/logo-flipped.png") ); }});

如果你遵循上述所有步骤,那么当你刷新页面时,网站的标志应该会上下翻转(见图 2-7)。图 2-7:使用另一张图片来重写图片请求

和之前一样,我们监听 fetch 事件,只不过这次我们寻找的请求是 /img/logo.png。当检测到这样的请求时,我们使用 fetch 命令创建一个新的请求,并传递另一个标志图片的 URL。fetch 会返回一个 promise,其中包含了新的响应,我们在 event.respondWith 方法中使用它来响应原本的请求。

换句话说,我们让 service worker 监听了标志图片的请求,并请求另一个标志图片作为代替,然后返回给浏览器窗口。fetch(request[, options]);fetch 方法的第一个参数是强制必传的,可以包含一个 request 对象,也可以包含一个相对路径或者绝对路径的 URL 字符串:// 通过URL请求fetch("/img/logo.png");// 通过request对象中的URL请求fetch(event.request.url);// 通过传递request对象请求// 在这个request对象中,除了URL,可能还包含了额外的头部信息、表单数据等fetch(event.request);第二个参数是可选的,可以包含一个对象,里面是请求的选项。下面这个例子发起了一个图片的 POST 请求,并在头部中包含了 cookie 信息(credentials 属性的默认值是 omit,这意味着 fetch 默认是不会发送 cookie 的):fetch("/img/logo.png", { method: "POST", credentials: "include"});fetch 会返回一个 promise,它在解析之后会得到一个响应对象。2.9 捕获离线请求

让我们使用刚才学到的关于 service worker 的所有内容,来检测用户何时处于离线状态,并向他呈现友好的错误消息,用来代替浏览器的默认错误提示。

我们从修改 serviceworker.js 的代码开始,对于所有的请求,简单地获取并返回它们原始请求的内容:self.addEventListener("fetch", function(event) { event.respondWith( fetch(event.request) );});

仔细阅读前面的代码,你可能会想知道这个事件的意义是什么。我们监听并捕获了所有的 fetch 事件,然后使用另一个完全相同的 fetch 操作进行响应。如果你在浏览器中查看这个网站,会看到网站的行为和我们添加 service worker 之前是完全一致的。

那么这有什么意义呢?你可能还记得,在上一个例子中,我提到了 fetch 返回的响应是包裹在一个 promise 中的。通过 promise 包裹响应之后,我们就可以在 promise 失败的时候捕获异常,并进行处理。

把 serviceworker.js 的代码替换成下列代码:self.addEventListener("fetch", function(event) { event.respondWith( fetch(event.request).catch(function() { return new Response( "Welcome to the Gotham Imperial Hotel.\n"+ "There seems to be a problem with your connection.\n"+ "We look forward to telling you about our hotel as soon as you go online." ); }) );});

刷新浏览器,确保最新版本的 service worker 已经被正确注册与安装,然后切换到离线状态(参见 2.4 节的“模拟离线状态”)并再次刷新页面。现在,你应该会看到哥谭帝国酒店的个性化消息,而不是浏览器的本地错误提示(见图 2-8)。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载