渐进式 Web 应用:实用指南

2025-05-25

渐进式 Web 应用:实用指南

有很多关于 PWA 的文章详细介绍了它是什么以及如何设置它。但你可能无法从一开始就理解它们。

我的目标是用简单的语言提供基本信息,让你理解什么是 PWA。然后,我会通过一个真实的例子来巩固你所学到的知识。所以,拿起你的笔记本电脑,跟着我一起学习吧。

阅读后您将了解以下内容:

  1. PWA 的优点和缺点。
  2. 基本概念。
  3. 如何在您的网站上配置这项技术。
  4. 如何更新缓存。

优点和缺点

与任何技术一样,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>
Enter fullscreen mode Exit fullscreen mode

布局已经准备好了,但是没有样式看起来不太好,所以我们也要添加样式。创建一个 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;
}
Enter fullscreen mode Exit fullscreen mode

然后将该文件连接到 index.html 的head标签中

<link rel="stylesheet" href="css/styles.css">

让我们立即连接所需的图片,这些图片可以在这里下载。点击链接,会出现一个绿色的“克隆或下载”按钮,点击它,然后点击“下载 ZIP”。压缩包将被下载,图片将保存在 images 文件夹中。呼,我想我解释得很清楚了:

下载 ZIP。

打开项目,在其中创建 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">
Enter fullscreen mode Exit fullscreen mode

因此,在文件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>
Enter fullscreen mode Exit fullscreen mode

现在只需运行并查看结果。我找到了一个非常方便的Chrome Web Server 扩展程序,它可以在本地运行服务器,您需要安装它,接下来我们会用到它。安装过程很简单,只需指定项目所在的文件夹,index.html 文件就在那里,他自己会做。复制链接并粘贴到浏览器中即可。

设置并运行 Web 服务器。

这就是我们得到的。我不会说这很好,但对我来说这很正常!

Mad Devs PWA。

好了,听着,最难的事情就算完成了,现在我们来看看 Google 验证对我们的工作有何评价。操作如下:按下F12 键,然后转到Lighthouse选项卡(在 Google 更新之前,这个选项卡名为“审核”),那里会有一个蓝色的“生成报告”按钮,点击一下。

Google 开发者工具:Lighthouse——生成报告。

验证过程完成后,我们将看到以下图片:负责 PWA 的项目将显示为灰色。这意味着我们没有任何设置。

Google 开发者工具:Lighthouse 报告 - 100% 性能。

如果你向下滚动,你会看到需要遵循的建议,以使 PWA 能够正常工作。

Lighthouse选项卡将帮助您在配置 PWA 时跟踪所有错误。

Google 开发者工具:Lighthouse 报告 - Progressivve Web App。

好吧,我们终于到了最有趣的部分

首先,需要在项目根目录中创建一个 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"
}
Enter fullscreen mode Exit fullscreen mode

好了,到此为止。以下是此文件所有属性的描述,以后有时间,请阅读。

插件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));
  }
});
Enter fullscreen mode Exit fullscreen mode

简单介绍一下那里发生的事情:

  • 页面加载完成后,我们会检查浏览器是否支持 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'
];
Enter fullscreen mode Exit fullscreen mode

接下来我们来看看事件。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);
    })
  );
});
Enter fullscreen mode Exit fullscreen mode

Fetch事件。此事件扫描所有请求,如果匹配缓存中的数据,则返回缓存中的匹配项。否则,返回来自服务器的数据。

respondWith方法负责从缓存或服务器返回的数据中获取数据。如果服务器没有返回任何数据,我们就从缓存中获取。

/* Serve cached content when offline */
self.addEventListener(‘fetch’, e => {
  e.respondWith(
    caches.match(e.request).then(response => {
      return response || fetch(e.request);
    })
  );
});
Enter fullscreen mode Exit fullscreen mode

这段代码目前已经足够了。现在让我们确保文件 sw.js 已注册,并且缓存已记录。前往开发者控制台,打开“应用程序”选项卡,然后前往“Service Workers”设置。在这里,我们可以看到文件 sw.js 已成功注册,并显示绿灯。

Google 开发者工具:应用程序。

我们继续沿着侧面导航面板移动,找到一个名为“缓存存储”的下拉列表,我们的缓存实际上存储在其中。点击它,您可以看到缓存了哪些文件和内容。

Google 开发者工具:应用程序。

现在,如果您禁用互联网并重新加载页面,该网站将会正常运行。

总结一下。为了让网站在没有网络的情况下也能正常工作,你不需要安装任何框架、添加库等等。只需几行代码和对这项技术的大致了解就足够了。

如何更新缓存?

我在处理 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!’);
    })
  );
});
Enter fullscreen mode Exit fullscreen mode

如果您阅读代码,您会看到一个条件,其中比较缓存名称,如果它们不匹配,则删除旧缓存。

关于activate事件,简单说一下。此事件在 Worker 注册并准备就绪后触发。但为了使其准备就绪,您需要等到网站不再使用旧缓存,这需要一些时间。为了避免这种等待,您可以添加下面的方法。

self.skipWaiting();
Enter fullscreen mode Exit fullscreen mode

现在,注册新 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);
    })
  );
});
Enter fullscreen mode Exit fullscreen mode

并在main.js文件中添加更新函数,该函数将在每次页面重新加载时开始更新缓存。

reg.update();
Enter fullscreen mode Exit fullscreen mode

添加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));
  }
});
Enter fullscreen mode Exit fullscreen mode

就这样,重新加载页面。再次打开开发者工具,在侧面板上的“Service Workers”选项卡中检查离线状态,然后重新加载页面,并查看“Cache Storage”选项卡。在这里,您可以看到旧缓存如何被新缓存替换。

安装新的工作程序大约需要 2 分钟,因此您可能需要多次重新加载页面。

Google 开发者工具:应用程序。

回到页面后,我们可以看到新的样式和更新后的缓存。好棒!

Google 开发者工具:应用程序。

结论

本文通过一个实际示例,介绍了如何创建和设置 PWA 技术的基本信息。欢迎在文章下方留言,分享你的 PWA 设置经验。

之前已在 Mad Devs IT 博客上发表过。

链接到 Vue Boilerplate。

文章来源:https://dev.to/maddevs/progressive-web-apps-practical-usage-guide-5238
PREV
Visual Studio Code 及其魔力
NEXT
TypeScript 比你想象的要多 TypeScript 类型系统语言(TSts)🟦