渐进式 Web 应用程序的构建块
已发表: 2022-03-10Web 应用可以一次性取代原生应用和网站的所有功能。 这些天来,它们越来越引人注目,但仍然没有足够多的人熟悉它们或采用它们。
在本文中,您将能够找到有关如何制作渐进式 Web 应用程序的一些注意事项,以及进一步研究的资源。 我还将讨论各种组件并支持围绕 Web 应用程序的问题。 尽管不是每个浏览器都对它们友好,但仍有一些令人信服的理由来了解更多关于这项技术的信息。
是什么让 Web 应用程序渐进式?
渐进式网络应用程序是某些技术的总称,这些技术共同在网络上产生类似应用程序的体验。 为简单起见,从现在开始,我将它们简称为 Web 应用程序。
理想的 Web 应用程序是具有 Web 和本地应用程序的最佳方面的网页。 它应该能够快速和快速地进行交互,适合设备的视口,保持离线可用,并且能够在主屏幕上有一个图标。
同时,它不能牺牲让网络变得伟大的东西,例如深入链接到应用程序的能力以及使用 URL 来实现内容共享的能力。 与网络一样,它应该跨平台运行良好,而不是仅仅专注于移动设备。 它在台式计算机上的表现应该与在其他形式因素中一样好,以免我们冒着进入另一个无响应的 m.example.com 网站时代的风险。
渐进式 Web 应用程序并不新鲜。 自 2011 年以来(Chrome Android 上为 2013 年),移动浏览器已经能够将网站添加到手机的主屏幕上, head
的元标记决定了已安装网页的外观。 自 2012 年以来,《金融时报》一直在使用网络应用程序在移动设备上交付数字内容。
迁移到 Web 应用程序后,《金融时报》能够使用同一个应用程序在单一分发渠道中跨平台发布。 当我在《金融时报》工作时,通过一个构建,我们能够支持以下内容:
- iOS,
- 安卓 (4.4+) 铬,
- 较旧的 Android(通过包装器),
- 视窗 8,
- 黑莓,
- 火狐操作系统。
真正做到了“一次构建,随处部署”。
“但它不在 App Store 中”
对于大多数大公司来说,用网站补充原生应用程序仍然是标准做法,这是有充分理由的。 其中包括对浏览器支持的担忧,以及大多数用户习惯使用本机应用程序这一事实。 稍后我将更详细地讨论这些问题。 这些担忧中最重要的是,如果应用程序不在应用商店中,它将如何获得曝光。
我会争辩说,在应用商店中并没有太大的优势,因为已经表明,如果你不在应用商店中排名前 0.1% 的应用程序中,那么你不会从那里获得显着的好处。
用户倾向于通过首先找到您的网站来找到您的应用程序。 如果您的网站是一个网络应用程序,那么它们已经到达目的地。
Web 应用程序的优势之一是,它使您能够通过减少在登陆您的网站和与您的应用程序互动之间重新吸引用户所需的点击次数来提高参与度。
通过让用户通过将其添加到他们的主屏幕来“安装”您的网络应用程序,他们可以继续与您的网站互动。 当他们关闭网络浏览器时,手机会向他们显示网络应用程序的安装位置,让您恢复他们的意识。
背景和当前气候
现代 Web 应用程序基于一种称为服务工作者的新技术。 服务工作者是位于用户标签和更广泛的互联网之间的可编程代理。 它们拦截并重写或制造网络请求,以允许非常精细的缓存和离线支持。
自 2011 年 Web 应用程序诞生以来,该应用程序可以将网站添加到主屏幕上,已经发生了许多发展,为创建渐进式 Web 应用程序奠定了更多基础。
Chrome 38 引入了 Web 应用清单,它是一个 JSON 文件,用于描述您的 Web 应用的配置。 这使我们能够从head
中删除配置。
在 Chrome 40(2014 年 12 月)中,服务工作者开始在 Firefox 和 Chrome 上推出。 截至撰写本文时,Apple 已选择不在 Safari 中实现此功能,但“正在考虑中”。 service worker 的作用是简化应用下线的过程; 它还为未来的类似应用程序的功能奠定了基础,例如推送通知和后台同步。
基于新服务工作者和 Web 应用程序清单构建的应用程序被称为渐进式 Web 应用程序。
渐进式 Web 应用程序与规范不同。 事实上,考虑到浏览器中内置的新技术,它一开始是对 Web 应用程序在服务工作者时代的定义。 具体来说,当满足多个条件时,Chrome 使用此定义在浏览器中触发安装提示。 条件是网络应用程序:
- 有一个服务工作者(需要 HTTPS);
- 有一个 Web 应用清单文件(至少具有最少的配置和
display: "standalone"
); - 有两次不同的访问。
在这种情况下,“渐进式”意味着浏览器支持的功能越多,体验就越接近应用程序。
目前在 Opera、Chrome 和三星浏览器的不同条件下会显示安装 Web 应用程序的提示。
Apple 已经表示对 iOS 的渐进式 Web 应用程序感兴趣,但在撰写本文时,它仍然依赖于元标记来配置 Web 应用程序和应用程序缓存 (AppCache) 以供离线使用。
网站在什么时候成为 Web 应用程序?
我们知道一个网站是什么样子,一个应用程序是什么样子,但是一个网站在什么时候变成了一个网络应用程序? 没有明确的衡量标准来说明什么是网络应用程序而不是网站。
在这里,我们将详细介绍 Web 应用程序的特征。
渐进式 Web 应用程序应该表现出某些类似应用程序的属性……
- 响应式
这些网站完美地填满了屏幕,主要针对手机和平板电脑,并且必须响应过多的屏幕尺寸。 它们也应该只用作桌面网站。 多年来,响应式设计一直是网站建设的重要组成部分。 Smashing Magazine 有一些很棒的文章。 - 离线优先
该应用程序必须能够离线启动并仍然显示有用的信息。 - 触控功能
界面应该是为触摸而设计的,带有手势交互。 用户交互必须感觉反应灵敏且快速,触摸和响应之间没有延迟。 - 应用元数据
该应用程序应该提供元数据来告诉浏览器它在安装时的外观,以便您在主屏幕上获得漂亮的高分辨率图标,并在某些平台上获得启动屏幕。 - 推送通知
该应用程序能够在应用程序未运行时接收通知(如果适用)。
......但应该保持某些类似网络的属性
- 进步
该应用程序的安装能力是一种渐进式增强。 至关重要的是,该应用程序仍可作为普通网站运行,尤其是在尚不支持安装或服务人员的平台上。 - 开放网络上的 HTTPS
该应用程序不应锁定到任何浏览器或应用程序商店。 它应该能够深度链接并提供共享当前 URL 的方法。
使您的网站离线
使您的网站离线会带来一些主要优势。
首先,当用户处于不稳定的网络连接时,它仍然可以工作。
此外,如果应用不依赖网络,从打开应用到使用应用的时间大大缩短。 这给用户带来了很好的体验。 如果最近使用过浏览器,经过精心优化的 Web 应用程序可以比原生应用程序启动得更快。
有两种方法可以让网站离线工作:
- 旧的和失败的方法
以 AppCache 的形式支持离线启动您的网站已有多年。 不过,AppCache 有一些严重的缺陷,甚至在规范中已被弃用。 它很难使用,如果配置错误,可能会永久破坏您的网站。 尽管如此,它仍然是在 iOS 上进行离线操作的唯一方法,至少在 Apple 采取行动支持服务人员之前是这样。 - 新热度
同样有效的是服务工作者,目前在 Chrome、Firefox 和 Opera 中得到支持,并且很快就会在 Edge 中推出。 Apple 的 WebKit 团队将其标记为“正在考虑中”。
Service Worker 与其他 Web Worker 一样,它们在单独的线程中运行,但它们不绑定到任何特定的选项卡。 它们在创建时被分配了一个 URL 范围,它们可以拦截和重写此范围内的任何请求。 如果您的服务工作者位于https://example.com/my-site/sw.js
,它将能够拦截对/my-site/
或更低级别的任何请求,但无法拦截对根https://example.com/
的请求https://example.com/
。
因为它们不绑定到任何选项卡,所以可以在后台激活它们以处理推送通知或后台同步。 尤其重要的是,它们不可能永久破坏您的网站,因为它们会在检测到新的 Service Worker 脚本时自动更新。
一个很好的指导原则是,如果您要从头开始构建新网站,请从服务人员开始。 但是,如果您的网站已经使用 AppCache 离线工作,那么您可以使用工具 sw-appcache-behavior 从中生成一个 Service Worker,因为我们可能很快就会到达一些浏览器只接受 Service Worker 而一些只接受应用缓存。
由于 AppCache 已被弃用,因此我不会在本文中进一步讨论它。
设置 Service Worker
(另请参阅“设置 Service Worker”以获取更详细的说明。)
因为 Service Worker 是一种特殊类型的共享 Web Worker,所以它在主页面的单独线程中运行。 这意味着它由与服务工作者在同一路径上的所有网页共享。 例如,位于/my-page/sw.js
的服务工作者将能够影响/my-page/index.html
和my-page/images/header.jpg
,但不会影响/index.html
。
Service Worker 能够拦截、重写或欺骗页面上的所有网络请求,包括对data://
URL 的请求!
这种能力使其能够在没有数据连接时提供缓存响应以使页面正常工作。 尽管如此,它仍然足够灵活,可以允许许多可能的用例。
它只允许在安全上下文(即 HTTPS)中使用,因为它非常强大。 这可以防止第三方使用来自受感染或恶意 Wi-Fi 接入点的注入服务人员永久覆盖您的网站。
现在设置 HTTPS 可能看起来令人生畏且昂贵,但实际上它从未如此简单或便宜。 Let's Encrypt 为您提供免费的 SSL 证书和脚本来自动配置您的服务器。 如果您在 GitHub 上托管,GitHub Pages 会自动通过 HTTPS 提供服务。 Tumblr 页面也可以配置为在 HTTPS 上运行。 CloudFlare 可以代理请求以升级到 HTTPS。
脱机通常涉及为网站的不同部分选择某些缓存方法,以便更快地提供服务或在没有 Internet 连接的情况下提供服务。 我将在下面讨论各种缓存方法。
我使用 Service Worker Toolbox 来抽象出复杂的缓存逻辑。 该库可以设置为通过提供四个预配置路由来处理路由,这些路由可以以干净的方式进行配置。 它可以被导入到你的 service worker 中。
用例 1:预缓存
在您的网站确定需要它们之前,预缓存会拉下请求。 这可以大大减少首次绘制时间,因为您的网站在开始下载您网站的徽标/images/logo.png
之前不需要解析/site.css
。
toolbox.precache(['/index.html', '/site.css', '/images/logo.png']);
用例 2:离线
在最简单的情况下,允许用户在离线时重新访问您的网站意味着在设备离线时回退到缓存。 在这里设置超时很重要,因为不稳定的网络、错误配置的路由器或强制门户可能会让用户无限期地等待。
toolbox.router.default = toolbox.networkFirst; toolbox.options.networkTimeoutSeconds = 5;
实际上,我们实际上可以更聪明一点,因为您的大部分资产不会随着时间而改变。 我们可能只想尽快获取内容,无论是来自缓存还是网络。 以下行告诉 Service Worker Toolbox,所有对图像路径的请求都应该来自缓存(如果它们在缓存中可用)。
toolbox.router.all('/images/*', toolbox.fastest);
在这种情况下,当用户进行身份验证时,重要的是我们不能只返回一个缓存的响应; 我们应该声明对/auth/
的请求应该是仅限网络的。
toolbox.router.post('/auth/*', toolbox.networkOnly);
以下是一些离线的良好做法:
- 初始静态资产应预先缓存。 这会在安装服务工作者时下载并缓存它们。 这意味着当最终需要它们时,不需要从服务器加载它们。
- 默认情况下,理想情况下,请求应该是从网络中新获取的,但会回退到缓存中,以便它们可以离线使用。
- 相对较短的网络超时意味着请求将能够在表示它有数据连接但没有返回响应的网络连接上返回缓存数据。
- 不经常更新的资产,例如图像,应该首先从缓存中分派,然后浏览器也会尝试更新它们。 如果使用
toolbox.cacheOnly
,那么它也可以保存用户的数据。
注意:浏览器缓存和缓存 API 是不同的动物。 在网络优先或仅网络的情况下,缓存 API 已被绕过。 该请求可能仍会命中浏览器的缓存,因为请求中的缓存标头表明它仍然有效。 这可能会导致用户接收到混合的缓存数据和新数据的问题。 Jake Archibald 有一些很好的建议来避免这个问题。
调试你的 Service Worker
如果您使用的是 Chrome 或 Opera,请转到chrome://serviceworker-internals
。 这将允许您检查、暂停和卸载您的服务工作者脚本。
在 Chrome 和 Opera 的最新版本中,您可以通过检查器中的“应用程序”选项卡获得详细的视图和调试工具。
交互和动画性能
人们已经开始期望 Web 没有原生应用程序所具有的流畅动画界面。 但是,Web 应用程序不能接受相同的标准。 Web 应用程序必须流畅地制作动画,以免我们的用户觉得我们正在提供一种降级的二流体验。 我们有三个目标让它感觉快:
- 当用户做某事时,应用程序也必须在 100 毫秒内做某事; 否则,用户会注意到延迟。 这对点击、点击、拖动和滚动都很重要。
- 每帧必须以一致的每秒 60 帧(每帧 16 毫秒)进行渲染。 即使是一些慢帧也会很明显。
- 它必须在一个运行了三年的廉价手机上运行在一个糟糕的网络上,而不仅仅是你闪亮的开发机器。
- 它必须快速启动。 长期以来,我们一直专注于通过让我们的网站在 1 到 2 秒内可见和可用来保持用户参与度。 对于 Web 应用程序,启动时间与以往一样重要。 如果应用程序的所有内容都被缓存并且浏览器仍在设备的内存中,那么 Web 应用程序可以比原生应用程序更快地启动。 本机应用程序和网站开发人员犯的一个错误是需要网络内容才能使产品工作。
- Web 应用程序必须很小才能下载和更新:从应用程序商店下载 10 MB 感觉并不多,但是对于很多移动用户来说,每次下载 10 MB 未缓存的 MB 是非常不可能的。
马上,最重要的项目就是这个,在文件的head
:
<meta name="viewport" content="width=device-width">
这条线确保在基于 Chromium 或 Firefox 的手机浏览器上没有 300 毫秒的点击延迟,但它仍然允许用户通过捏合来放大(非常适合可访问性)。
根据某些启发式方法,自从 iOS 8 推出以来,默认情况下点击速度很快,但如果点击速度很快,则可能看起来很慢。 如果这对您来说是个问题,我建议使用 FastClick 来消除延迟。
还有动画性能的问题。 你可能会想要很多漂亮的元素动画进出,用户可以拖动的元素,以及其他可爱的交互。
Web 性能可以非常详细地讨论,并且是我非常关心的一个主题,但我不会在这里详细介绍。 我将谈谈我所做的以确保我的交互和动画清晰流畅。
四处寻找或向您的朋友或家人询问旧智能手机。 我通常借用我伙伴的 Nexus 4。
将手机插入计算机,然后转到chrome://inspect/#devices
。 这将打开一个检查窗口,您可以使用该窗口检查手机上运行的网页。 您可以使用分析和查看时间线来查找性能不佳的来源,然后根据真实的基线设备对其进行优化。
动画某些 CSS 属性是抖动动画的最大原因之一,称为 jank。 CSS 触发器是一个很好的资源,用于确定哪些属性可以安全地进行动画处理,而不会导致浏览器重新绘制或重新布局整个页面。
如果编写高性能动画对您来说是一项艰巨的任务,那么许多库可以处理这项工作。 我最喜欢的是 GreenSock,它可以很好地处理触摸交互,例如拖动项目,并且可以做非常花哨的动画和补间。
推送通知
推送通知是重新与用户互动的好方法。 通过提示用户,您可以将您的应用程序带到他们的脑海中。 他们可以完成未完成的交易或接收有关相关新内容的警报。
确保您的推送通知与用户当时发生的事件相关。 不要把时间浪费在以后可以做的事情上。 您通知他们的内容应该需要他们采取行动(回复某人或参加活动)。 此外,如果您的 Web 应用程序可见或处于焦点,请不要推送通知。
与之交互时,通知应将用户带到离线工作的页面。 通知可以悬而未决; 当用户没有网络连接时,它们可能会被交互。 如果您的推送通知在尝试与之交互后拒绝工作,用户会感到沮丧。
推送通知的最佳体验让用户完全不必打开您的网络应用程序! “你有一条新消息”是无用的,就像点击诱饵标题一样烦人。 显示消息和发件人。
通知中的操作按钮可以提供不一定打开浏览器的交互提示(“喜欢这篇文章”、“回复是”、“回复不是”、“稍后提醒我”)。 这些以他们的条件为用户服务,让他们保持参与并最大限度地减少他们的时间投资。
如果您使用常规或不相关的通知向用户发送垃圾邮件,他们可能会在浏览器中禁用您的应用程序的通知。 在那之后,几乎不可能再次与他们互动,并且您将无法轻松再次提示他们获得许可!
为避免这种情况,请让您的应用程序的“禁用通知”按钮的路径清晰且简单。 一旦你解决了任何让用户感到沮丧的问题,你就可以尝试重新参与。
推送通知 API 需要一个服务工作者。 因为在没有打开浏览器选项卡时可以接收推送通知,所以服务工作者将在后台线程中处理通知请求。 它可以执行异步操作,例如在向用户显示通知之前向您的 API 发出 fetch 请求。
要创建推送通知,请向浏览器制造商提供的端点发出请求。 对于基于 Chromium 的浏览器(Opera、Samsung 和 Chrome),这将是 Firebase Cloud Messaging。 这些浏览器的行为也有些不合规格。
可以通过请求推送通知权限来找到这方面的详细信息:
serviceWorkerRegistration .pushManager .subscribe({ // Required parameter as receiving updates // but not displaying a message is not supported everywhere. userVisibleOnly: true }) .then(function(subscription) { return sendSubscriptionToServer(subscription); })
订阅是一个如下所示的对象:
{ "endpoint": "https://example.com/some/uuid" }
在上面的示例中, uuid
唯一标识当前正在使用的浏览器。
注意:基于 Chromium 的浏览器的行为略有不同。 您需要一个 Firebase 应用 ID,该 ID 需要在您的 Web 应用的清单文件中输入(例如, "gcm_sender_id": "90157766285"
)。
此外,端点将无法按照给定的格式工作。 您的服务器需要稍微修改它才能使其正常工作。 它需要是一个POST
请求,您的 API 密钥在head
中, uuid
在body
中。
发送推送通知时,可以在推送通知本身的正文中发送数据。 这很复杂,涉及对 API 请求中的内容进行加密。 Node.js 的 web-push 包可以处理这个问题,但我不喜欢它。
一旦收到通知就可以执行异步请求,所以我更喜欢向客户端发送一个最小的通知,称为“tickle”,然后服务工作者将向我的 API 发出获取请求以提取任何最近更新。
注意:服务工作者可以随时被浏览器关闭。 push 事件中的event.waitUntil
函数告诉 service worker 在事件完成之前不要关闭。
self.addEventListener('push', function(event) { event.waitUntil( // Makes API request, returns Promise getMessageDetails() .then(function (details) { self.registration.showNotification( details.title, { body: details.message, icon: '/my-app-logo-192x192.png' }) }) ); });
您也可以在通知事件上收听单击或按下交互。 使用这些来决定如何回应。 您可以打开新的浏览器选项卡、聚焦现有选项卡或发出 API 请求。 您还可以选择是关闭通知还是保持打开状态。 将此功能与通知上的操作一起使用,以在通知本身中构建出色的功能。 这将带来很好的体验,因为用户不需要每次都打开您的应用程序。
不要忽视网络的优势
最后也是最重要的一点是,在我们急于获得类似应用程序的体验时,我们不应该忘记在一个非常重要的方面保持类似网络:URL。
已安装的网络应用程序通常会隐藏浏览器外壳。 没有地址栏供用户分享当前网址,用户无法保存当前页面以备后用。
URL 具有独特的网络优势:您可以通过单击而不是描述如何导航来让人们使用您的应用程序。 尽管如此,很容易忘记将其暴露给用户。 您可以编写世界上最好的应用程序,但如果没有人可以分享它,您将对自己造成重大伤害。
归结为:提供通过共享按钮或公开 URL 的按钮来共享您的应用程序的方法。
不支持渐进式 Web 应用程序的浏览器怎么办?
查看 ServiceWorker 准备好了吗? 跨浏览器对服务工作者的支持的当前状态。
你可能已经注意到,我提到了 Chrome、Firefox 和 Edge,但没有提到 Safari。 Apple 向世界推出了 Web 应用程序,并对渐进式 Web 应用程序表现出了兴趣,但它仍然不支持服务工作者或 Web 应用程序清单。 你能做什么?
可以使用 AppCache 为 Safari 创建一个离线网站,但这样做既困难又充满奇怪的边缘情况,这些情况可能会破坏页面或在首次加载后使其永久过期。
相反,构建出色的 Web 应用程序体验。 您的工作不会被浪费,因为在 Safari 中体验仍然很棒,这是一个非常好的浏览器。 当服务人员确实来到 Safari 时,您将准备好利用他们。
最后,我们可以期待 Web 应用程序世界中许多令人兴奋的发展,随着对它们背后的技术的支持越来越多,以及 Web 平台的新功能,例如用于与硬件交互的 Web 蓝牙 API、用于虚拟的 WebVR现实,以及用于高速游戏的 WebGL 2。 现在是探索网络应用的可能性并参与塑造网络未来的大好时机。
链接
渐进式 Web 应用程序的其他写作
- “又一个关于渐进式 Web 应用的状态和未来的博客,”Ada Rose Edwards
文章中提到的链接
- “App Store 的形状”,查尔斯·佩里
- Service Worker Helpers, Google, GitHub
- Service Worker 工具箱、谷歌、GitHub
- “300 毫秒的点击延迟和 iOS 8,”TJ VanToll
- “缓存最佳实践和最大年龄陷阱”,Jake Archibald