知海

渐进式 Web 应用(PWA)

webpackjsorg-main指南-教程

渐进式 Web 应用(PWA)

本指南基于输出管理指南中的代码示例展开。

渐进式 Web 应用(Progressive Web Application,PWA)是能够提供类似原生应用体验的 Web 应用。有很多因素可以促成这种体验,其中最重要的是应用能够离线运行。这是通过一种名为 Service Workers 的 Web 技术实现的。

本节将重点为我们的应用添加离线体验。我们将使用 Google 的名为 Workbox 的项目来实现这一目标,该项目提供的工具可以帮助我们更轻松地为 Web 应用设置离线支持。

当前无法离线工作

到目前为止,我们一直都是直接通过本地文件系统查看输出。但通常情况下,真实用户是通过网络访问 Web 应用的;他们的浏览器会与服务器通信,由服务器提供所需的资源(例如 .html.js.css 文件)。

因此,让我们使用一个功能更基础的服务器来测试当前的体验。我们使用 http-server 包:npm install http-server --save-dev。同时,我们还要修改 package.json 中的 scripts 部分,添加一个 start 脚本:

package.json

diff 复制代码
{
  ...
  "scripts": {
-    "build": "webpack"
+    "build": "webpack",
+    "start": "http-server dist"
  },
  ...
}

注意:webpack DevServer 默认写入内存。我们需要启用 devserverdevmiddleware.writeToDisk 选项,以便 http-server 能够从 ./dist 目录提供文件。

如果之前还没有构建过项目,请先运行 npm run build 构建项目,然后运行 npm start。执行后会得到类似下面的输出:

bash 复制代码
> http-server dist

Starting up http-server, serving dist
Available on:
  http://xx.x.x.x:8080
  http://127.0.0.1:8080
  http://xxx.xxx.x.x:8080
Hit CTRL-C to stop the server

如果你在浏览器中打开 http://localhost:8080(即 http://127.0.0.1),应该会看到 webpack 应用正在从 dist 目录提供服务。如果你停止服务器并刷新页面,webpack 应用将不再可用。

这正是我们要改变的地方。完成本模块的学习后,即使停止服务器并刷新页面,也应该仍然能够看到我们的应用。

添加 Workbox

让我们添加 Workbox webpack 插件,并调整 webpack.config.js 文件:

bash 复制代码
npm install workbox-webpack-plugin --save-dev

webpack.config.js

diff 复制代码
  import path from "node:path";
  import { fileURLToPath } from 'node:url';
  import HtmlWebpackPlugin from "html-webpack-plugin";
+ import WorkboxPlugin from "workbox-webpack-plugin";

 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

  export default {
    entry: {
      app: './src/index.js',
      print: './src/print.js',
    },
    plugins: [
      new HtmlWebpackPlugin({
-       title: 'Output Management',
+       title: 'Progressive Web Application',
      }),
+     new WorkboxPlugin.GenerateSW({
+       // these options encourage the ServiceWorkers to get in there fast
+       // and not allow any straggling "old" SWs to hang around
+       clientsClaim: true,
+       skipWaiting: true,
+     }),
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
  };

完成上述配置后,让我们运行 npm run build,看看会发生什么:

bash 复制代码
...
                  Asset       Size  Chunks                    Chunk Names
          app.bundle.js     545 kB    0, 1  [emitted]  [big]  app
        print.bundle.js    2.74 kB       1  [emitted]         print
             index.html  254 bytes          [emitted]
precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js  268 bytes          [emitted]
      service-worker.js       1 kB          [emitted]
...

如你所见,现在会额外生成两个文件:service-worker.js 和内容更详细的 precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.jsservice-worker.js 是 Service Worker 文件,而 precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.jsservice-worker.js 运行所需的文件。你自己生成的文件可能会有所不同,但目录中应该会有一个 service-worker.js 文件。

至此,我们已经成功生成了一个 Service Worker。接下来该做什么?

注册 Service Worker

让我们通过注册 Service Worker 来让它正式生效。我们将添加以下注册代码:

index.js

diff 复制代码
  import _ from 'lodash';
  import printMe from './print.js';

+ if ('serviceWorker' in navigator) {
+   window.addEventListener('load', () => {
+     navigator.serviceWorker.register('/service-worker.js').then(registration => {
+       console.log('SW registered: ', registration);
+     }).catch(registrationError => {
+       console.log('SW registration failed: ', registrationError);
+     });
+   });
+ }

再次运行 npm run build,构建包含注册代码的应用版本,然后使用 npm start 提供服务。打开 http://localhost:8080 并查看控制台,你应该能在其中看到:

bash 复制代码
SW registered

现在来测试一下。停止服务器并刷新页面。如果你的浏览器支持 Service Worker,你仍然应该能够看到你的应用。不过,此时它是由 Service Worker 提供的,而不是由服务器提供的。

结论

你已经使用 Workbox 项目构建了一个离线应用,并开启了将 Web 应用转变为 PWA 的旅程。现在,你或许想要进一步拓展。这里有一个很好的资源可以帮助你:https://web.dev/progressive-web-apps/

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。