渐进式 Web 应用:实用指南
有很多关于 PWA 的文章详细介绍了它是什么以及如何设置它。但你可能无法从一开始就理解它们。
我的目标是用简单的语言提供基本信息,让你理解什么是 PWA。然后,我会通过一个真实的例子来巩固你所学到的知识。所以,拿起你的笔记本电脑,跟着我一起学习吧。
阅读后您将了解以下内容:
- PWA 的优点和缺点。
- 基本概念。
- 如何在您的网站上配置这项技术。
- 如何更新缓存。
优点和缺点
与任何技术一样,PWA 也有其优点和局限性。在介绍 PWA 之前,我们先来了解一下什么是 PWA。
PWA技术由谷歌于2015年发布,它将自己定位为一个附加组件,可以让你的网站看起来像一个移动应用程序。
同时,内部没有改变,没有变换,网站保持不变,只有浏览器发生了变化。
此外,您还应该了解哪些类型的业务比移动应用程序更适合 PWA。点击此处了解更多关于 PWA 与移动应用程序之间差异的信息。
PWA 能做什么?
- 发送通知、缓存内容、设置桌面快捷方式;
- 以弹出窗口的形式发送通知,您可以通知用户一些事情;
- 由于内容缓存,可以离线工作,即无需互联网连接。
PWA 优势
- PWA安装非常简单。你无需前往任何应用商店,下载任何东西,也无需费力地敲鼓。只需点击链接打开网站,系统会弹出“安装桌面快捷方式”的窗口,安装即可完成。
- 它适用于所有或多或少现代的设备,您只需要一个浏览器。
- 由于桌面上有快捷方式,网站访问起来更加方便。你解锁手机,点击快捷方式,网站就打开了。太棒了。
- 占用内存较少,不到1MB。
- 与开发移动应用相比,PWA 的开发时间更短。无需在 Android 和 iOS 上分别编写两个完全相同的应用。因此,对企业来说,成本会更低。
- 更高的安全性——所有资源仅通过 https 传输。
- 运行稳定。如果网络出现问题,内容将从缓存中补偿,因此网站始终可用。
PWA 的缺点
- 有一种误解认为 PWA 有助于提升 SEO 效果。我不同意!SPA首先会遇到一个问题,即 HTML 标记在 JavaScript 渲染页面上。在脚本加载完成(需要加载时)之前,布局不会显示,只会显示
<div>
“app”ID。这时,一切都变得愚蠢起来,SEO 分析开始了,但正如你所理解的,页面是空的。即使你在网站上添加 +100500 PWA,它们也不会加快 HTML 代码的渲染速度。为了避免这种说法不实,我们通过一个真实的例子来验证。以madops.io网站为例,它是一个单页应用程序。如果你查看它的内部源代码:https://madops.io,你会看到我上面描述的所有内容。在其他情况下,如果服务器一次性渲染所有 HTML 标记,则不会出现问题,例如,查看源代码:https://maddevs.io。*功能障碍。出于安全原因,PWA 将无法使用摄像头控制、短信发送、传感器访问等功能。* 仍有一些浏览器不支持 PWA。例如,iOS 上的推送通知。
如果您想了解有关 PWA 的更多信息,请查看此链接。
基本概念
在深入研究 PWA 设置之前,让我们先了解一下它的基本概念及其组件。
Service Worker——本质上是一个脚本文件,负责所有这些神奇的功能。所有浏览器请求都会经过它,这提供了许多可能性,例如,如果没有网络连接,它会从缓存中返回内容(当然,前提是缓存中有缓存)。
我们处理各种事件,从缓存中写入、删除文件等等。
脚本在后台与应用程序并行运行。
manifest.json — 设置文件。我们在这里指定要使用的图标、快捷方式中显示的文本、打开浏览器窗口的格式等等。下文我们将更详细地讨论它。
应用程序外壳——这是 PWA 的外壳名称。更具体地说,它是一个经过轻微改造的浏览器,可以为开发者提供更多功能。
HTTPS — PWA 的主要要求之一是通过 https 协议传输数据,该协议更安全。
您可以使用 localhost 进行开发。
推送通知——发送推送通知的技术。
设置 PWA
PWA 的设置非常简单。那就从编写代码开始吧!
不,等等。
这是现成代码的链接:https://github.com/denisoed/PWA-example。在这里,您可以下载后续需要的图像,以便您了解发生了什么。
首先,你需要在项目中创建一个文件夹,例如命名为 PWA。然后向此文件夹添加 index.html,其中包含以下代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PWA</title>
<meta name="description" content="Progressive Web Apps">
</head>
<body class="fullscreen">
<div class="container">
<a href="https://maddevs.io" target="_blank">
<img src="./images/logo.svg" alt="Mad Devs">
</a>
<h1>PWA</h1>
<p>Progressive Web Apps</p>
</div>
</body>
</html>
布局已经准备好了,但是没有样式看起来不太好,所以我们也要添加样式。创建一个 CSS 文件夹,在其中添加 style.css 文件并插入以下代码:
body {
font-family: sans-serif;
}
/* Make content area fill the entire browser window */
html,
.fullscreen {
display: flex;
height: 100%;
margin: 0;
padding: 0;
width: 100%;
background-color: #000;
}
/* Center the content in the browser window */
.container {
margin: auto;
text-align: center;
}
.container img {
width: 50px;
height: auto;
}
.container h1 {
color: #fff;
font-size: 12rem;
font-weight: bold;
margin: 30px 0 -20px;
}
.container p {
color: #fff;
font-size: 3rem;
margin: 0;
}
然后将该文件连接到 index.html 的head标签中
<link rel="stylesheet" href="css/styles.css">
让我们立即连接所需的图片,这些图片可以在这里下载。点击链接,会出现一个绿色的“克隆或下载”按钮,点击它,然后点击“下载 ZIP”。压缩包将被下载,图片将保存在 images 文件夹中。呼,我想我解释得很清楚了:
打开项目,在其中创建 images 目录,将所有图片插入其中。然后打开 index.html 并将元信息插入head标签。元信息是什么以及为什么插入,您可以在这里阅读。
<link rel="icon" href="images/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="images/mstile-150x150.png">
<meta name="theme-color" content="black" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="PWA">
<meta name="msapplication-TileImage" content="images/mstile-144x144.png">
<meta name="msapplication-TileColor" content="#FFFFFF">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
因此,在文件index.html中,应该有如下结构:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PWA</title>
<meta name="description" content="Progressive Web Apps">
<link rel="icon" href="images/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="images/mstile-150x150.png">
<meta name="theme-color" content="black" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="PWA">
<meta name="msapplication-TileImage" content="images/mstile-144x144.png">
<meta name="msapplication-TileColor" content="#000">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/styles.css">
</head>
<body class="fullscreen">
<div class="container">
<a href="https://maddevs.io" target="_blank">
<img src="./images/logo.svg" alt="Mad Devs">
</a>
<h1>PWA</h1>
<p>Progressive Web Apps</p>
</div>
</body>
</html>
现在只需运行并查看结果。我找到了一个非常方便的Chrome Web Server 扩展程序,它可以在本地运行服务器,您需要安装它,接下来我们会用到它。安装过程很简单,只需指定项目所在的文件夹,index.html 文件就在那里,他自己会做。复制链接并粘贴到浏览器中即可。
这就是我们得到的。我不会说这很好,但对我来说这很正常!
好了,听着,最难的事情就算完成了,现在我们来看看 Google 验证对我们的工作有何评价。操作如下:按下F12 键,然后转到Lighthouse选项卡(在 Google 更新之前,这个选项卡名为“审核”),那里会有一个蓝色的“生成报告”按钮,点击一下。
验证过程完成后,我们将看到以下图片:负责 PWA 的项目将显示为灰色。这意味着我们没有任何设置。
如果你向下滚动,你会看到需要遵循的建议,以使 PWA 能够正常工作。
Lighthouse选项卡将帮助您在配置 PWA 时跟踪所有错误。
好吧,我们终于到了最有趣的部分
首先,需要在项目根目录中创建一个 manifest.json 文件。我们向其中添加以下元数据:
- name — 全名。用于应用程序快捷方式;
- short_name — 当全名不适合时,将使用缩写名称;
- 图标——已安装应用程序的快捷方式中显示的图标列表;
- lang — 默认语言;
- start_url — 必需参数。它告诉应用程序从哪些文件启动。打开应用程序时,浏览器始终会打开此页面;
- display — 指示以哪种格式打开浏览器窗口;
- background_color——当应用程序首次在移动设备上启动时,此属性用于屏幕保护程序;
- theme_color — 设置工具栏的颜色,可在任务切换的应用预览中显示。theme_color必须与文档标题中指定的元主题颜色匹配。在本例中,
<meta name= “theme-color” content= “black” />
{
"name": "Progressive Web Apps",
"short_name": "PWA",
"icons": [
{
"src": "images/mstile-70x70.png",
"sizes": "70x70",
"type": "image/png"
},
{
"src": "images/mstile-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "images/mstile-150x150.png",
"sizes": "150x150",
"type": "image/png"
},
{
"src": "images/mstile-192x192.png",
"sizes": "310x150",
"type": "image/png"
},
{
"src": "images/mstile-310x150.png",
"sizes": "310x150",
"type": "image/png"
},
{
"src": "images/mstile-310x310.png",
"sizes": "310x310",
"type": "image/png"
},
{
"src": "images/mstile-512x512.png",
"sizes": "310x310",
"type": "image/png"
}
],
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"background_color": "black",
"theme_color": "black"
}
好了,到此为止。以下是此文件所有属性的描述,以后有时间,请阅读。
插件manifest.json在index.html的head标签中
<link rel="manifest" href="/manifest.json">
让我们开始编写脚本。创建一个名为 js 的文件夹,在其中添加包含以下代码的 main.js 文件:
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('SW registered!');
}).catch(err => console.log('SW registration FAIL:', err));
}
});
简单介绍一下那里发生的事情:
- 页面加载完成后,我们会检查浏览器是否支持 serviceWorker,如果支持,则继续下一步。*然后注册 sw.js 文件(目前尚未配置)。这没什么特别的。
在 index.html 中启用脚本,但不再位于 head 标签中,而是位于结束 body 标签之前。
<script src="js/main.js"></script>
好了,现在让我们创建文件 sw.js。它将存储 Service Worker 的所有逻辑。在项目根目录中创建它,并将缓存名称添加为第一行。
const cacheName = 'pwa_v1';
下一行,添加includeToCache变量。在其中,我们指定要缓存的文件。是的,我知道这不太方便,我必须手动注册所有文件,但我们有现成的。这样可以确保不会缓存任何多余的文件。这样既节省流量,又保证稳定性。
const includeToCache = [
'/',
'/index.html',
'/images/favicon.ico',
'/images/logo.svg',
'/images/logo-black.svg',
'/css/styles.css',
'/js/main.js'
];
接下来我们来看看事件。Service Worker 内部有多个事件,也称为生命周期。其中第一个是install。它仅在写入缓存时触发一次。
/* Start the service worker and cache all of the app's content */
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(includeToCache);
})
);
});
Fetch事件。此事件扫描所有请求,如果匹配缓存中的数据,则返回缓存中的匹配项。否则,返回来自服务器的数据。
respondWith方法负责从缓存或服务器返回的数据中获取数据。如果服务器没有返回任何数据,我们就从缓存中获取。
/* Serve cached content when offline */
self.addEventListener(‘fetch’, e => {
e.respondWith(
caches.match(e.request).then(response => {
return response || fetch(e.request);
})
);
});
这段代码目前已经足够了。现在让我们确保文件 sw.js 已注册,并且缓存已记录。前往开发者控制台,打开“应用程序”选项卡,然后前往“Service Workers”设置。在这里,我们可以看到文件 sw.js 已成功注册,并显示绿灯。
我们继续沿着侧面导航面板移动,找到一个名为“缓存存储”的下拉列表,我们的缓存实际上存储在其中。点击它,您可以看到缓存了哪些文件和内容。
现在,如果您禁用互联网并重新加载页面,该网站将会正常运行。
总结一下。为了让网站在没有网络的情况下也能正常工作,你不需要安装任何框架、添加库等等。只需几行代码和对这项技术的大致了解就足够了。
如何更新缓存?
我在处理 PWA 时遇到的第一个问题是更新旧缓存。但事实证明,这个问题很容易解决。
让我们更改一些样式,以便您看到一些变化。然后刷新页面,确保页面上的样式已更改。我们断开网络连接并重新加载页面,但由于某种原因,缓存未更新,我们看到的是旧版本的网站。
解决方案是在 sw.js 文件中添加activate事件,调用时会检查新旧缓存的名称,如果名称不同,则删除旧缓存并添加新缓存。是的,为了更新缓存,我们需要在每次更新代码时更改其名称。
一开始,我没有在缓存名称中指定后缀 * _v1,这完全是无稽之谈,因为这就是它的版本。无论你叫什么都没关系,只要名称不同就行。
self.addEventListener(‘activate’, e => {
// delete any caches that aren’t in cacheName
// which will get rid of version
e.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (cacheName !== key) {
return caches.delete(key);
}
})
)).then(() => {
console.log(cacheName + ‘ now ready to handle fetches!’);
})
);
});
如果您阅读代码,您会看到一个条件,其中比较缓存名称,如果它们不匹配,则删除旧缓存。
关于activate事件,简单说一下。此事件在 Worker 注册并准备就绪后触发。但为了使其准备就绪,您需要等到网站不再使用旧缓存,这需要一些时间。为了避免这种等待,您可以添加下面的方法。
self.skipWaiting();
现在,注册新 Worker 后缓存将立即更新。将其添加到install事件中。
/* Start the service worker and cache all of the app's content */
self.addEventListener('install', e => {
self.skipWaiting();
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(includeToCache);
})
);
});
并在main.js文件中添加更新函数,该函数将在每次页面重新加载时开始更新缓存。
reg.update();
添加console.log()方法。其实这无所谓,主要还是在回调.then()里
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.update();
console.log('SW registered!');
}).catch(err => console.log('SW registration FAIL:', err));
}
});
就这样,重新加载页面。再次打开开发者工具,在侧面板上的“Service Workers”选项卡中检查离线状态,然后重新加载页面,并查看“Cache Storage”选项卡。在这里,您可以看到旧缓存如何被新缓存替换。
安装新的工作程序大约需要 2 分钟,因此您可能需要多次重新加载页面。
回到页面后,我们可以看到新的样式和更新后的缓存。好棒!
结论
本文通过一个实际示例,介绍了如何创建和设置 PWA 技术的基本信息。欢迎在文章下方留言,分享你的 PWA 设置经验。
之前已在 Mad Devs IT 博客上发表过。
文章来源:https://dev.to/maddevs/progressive-web-apps-practical-usage-guide-5238