如何在create-react-app中使用Workbox构建自定义PWA

Note: This is the third in a series of posts about PWAs inside of React. For a quick primer, see the previous two posts here and here.

注意:这是有关React内部PWA的系列文章中的第三篇。 有关快速入门,请参见此处此处的前两篇文章。

In this follow up post, I’m going to take you through how to build a custom Progressive Web App (PWA) using Google’s Workbox library without ejecting from the create-react-app (CRA) shell.

在这篇后续文章中,我将向您介绍如何使用Google的Workbox库构建自定义的渐进式Web应用程序(PWA),而无需从create-react-app(CRA)外壳弹出。

Workbox is a collection of libraries that make it easier to build offline functionality. Workbox is also considered the successor to the sw-precache library, which CRA uses to generate a default SW.

Workbox是库的集合,这些库使构建脱机功能更加容易。 Workbox也被视为sw-precache库的后继程序,CRA使用该库来生成默认SW。

There has been some talk about CRA migrating from sw-precache to Workbox (reference this issue for details). Unfortunately, nothing seems to have come of it quite yet.

有一些关于CRA从sw-precache迁移到Workbox的讨论(有关详细信息,请参阅此问题 )。 不幸的是,到目前为止似乎还没有什么。

目标 (Goals)

  1. Configure the CRA build to use react-app-rewired. (react-app-rewired is a library to configure the default CRA build without ejecting)

    将CRA构建配置为使用react-app-rewired 。 (react-app-rewired是用于配置默认CRA构建而不弹出的库)

  2. Use react-app-rewired to customize the build to use Workbox to generate a service worker

    使用react-app-rewired自定义构建以使用Workbox生成服务工作者
  3. Build a very simple todo app

    构建一个非常简单的待办应用
  4. Implement offline functionality for the todo app using Workbox.

    使用Workbox为待办事项应用程序实现离线功能。

    The offline functionality we will be targeting:

    我们将针对的离线功能:

    a) Cache retrieved assets so they can be served offline

    a)缓存检索到的资产,以便可以离线提供它们

    b) Allow POSTing of data offline

    b)允许离线发布数据

将Workbox引入CRA (Introducing Workbox into CRA)

First, create a fresh CRA repository with the following command:

首先,使用以下命令创建一个新的CRA存储库:

npx create-react-app react-app-rewire-workbox

This should set up a new folder with the relevant name. Once you have this folder set up, cd into the folder and create a service worker file in the public folder. I’ll call mine custom-service-worker.js.

这应该使用相关名称设置一个新文件夹。 设置完此文件夹后,请CD进入该文件夹,并在公用文件夹中创建一个Service Worker文件。 我将其命名为custom-service-worker.js

Once, you’ve done this, go ahead and remove the check for NODE_ENV being set to PRODUCTION inside of registerServiceWorker.js

有一次,你这样做,继续和消除对检查NODE_ENV被设置为PRODUCTION内registerServiceWorker.js

Finally, inside of the custom-service-worker.js file, paste the following code:

最后,在custom-service-worker.js文件中,粘贴以下代码:

This code snippet is something I’ve picked up straight from the Workbox website. You use the importScripts line to inject a global variable named workbox into your file. The script your are importing is served via a CDN. You then have a simple check to see if the variable was loaded correctly by the script or not.

该代码段是我直接从Workbox网站上获得的 。 您可以使用importScripts线注入一个全局命名变量workbox到您的文件。 您要导入的脚本通过CDN提供。 然后,您可以进行简单检查,以查看脚本是否正确加载了变量。

So, we now have Workbox working for us in a dev environment. Next, let’s figure out how to implement react-app-rewired into CRA.

因此,现在我们有了Workbox在开发环境中为我们工作。 接下来,让我们弄清楚如何在CRA中实现react-app-rewired

在CRA中实施react-app-rewired (Implementing react-app-rewired In CRA)

Add the react-app-rewired package to your project folder by using the following command:

使用以下命令将react-app-rewired软件包添加到您的项目文件夹中:

npm install --save-dev react-app-rewired

Now, if you read the docs, they mention that you need to set up a config-overrides.js file in the root directory of your project. Let’s figure out what this does first.

现在,如果您阅读docs ,他们会提到您需要在项目的根目录中设置config-overrides.js文件。 让我们先弄清楚它的作用。

I’ll set up a barebones file and explain to you what it means. There is a very detailed explanation of this in the docs, if you wish to read that instead.

我将设置一个准系统文件,并向您解释它的含义。 如果您想阅读的话,文档中对此有非常详细的解释。

You can export an object from this file with three keys: webpack, jest, devServer. The respective functions allow you to configure the webpack production server configuration, the jest configuration, and finally the webpack development server configuration.

您可以使用以下三个键从该文件中导出对象:webpack,jest,devServer。 各自的功能允许您配置webpack生产服务器配置,临时配置,最后配置webpack开发服务器配置。

If you look at the devServer key in the config-overrides.js file, you will notice that we are logging configFunction.toString() instead of just configFunction . This is because if you try the latter, Node will just print [Function] to the console.

如果查看config-overrides.js文件中的devServer项,您会注意到我们正在记录configFunction.toString()而不是configFunction 。 这是因为如果您尝试使用后者,Node只会将[Function]打印到控制台。

Open up your package.json file and replace the scripts command for start with react-app-rewired start .

打开您的package.json文件,并使用react-app-rewired start替换用于启动的scripts命令。

构建Todo应用 (Building The Todo App)

So far, we have managed to introduce Workbox into our dev environment, and have also introduced react-app-rewired into our CRA shell. Let’s leave things as they are and build a sample todo app, and get it running in the dev environment.

到目前为止,我们已经设法将Workbox引入我们的开发环境中,并且还已经将react-app-rewired引入了我们的CRA shell中。 让我们保持现状,构建一个示例待办应用程序,并使其在开发环境中运行。

The todo app is going to need a couple of moving pieces, just so we can actually make use of service workers.

todo应用程序将需要几个动作,因此我们实际上可以利用服务人员。

It’s going to involve:

它涉及:

  1. A basic UI layer (I’m going to completely ignore styling for this.)

    一个基本的UI层(为此,我将完全忽略样式。)
  2. A json-server we can request data from

    我们可以从json-server请求数据

I’m not going into too much detail about setting this up, because its all fairly straightforward. I’ll include a link to a git repo with a working version of this app at the end of this article, so you can have a look at that.

我不会在此设置太多细节,因为它非常简单。 在本文结尾处,我将提供一个指向git repo的链接以及该应用程序的工作版本,因此您可以进行查看。

Here is the Todo component I have written.

这是我写的Todo组件。

The component makes a fetch request to a json-server I have set up, and gets a response consisting of an array of todos. The component then renders these todos. Like I said, extremely simple.

该组件向我已设置的json-server发出提取请求,并获取包含待办事项数组的响应。 然后,组件将渲染这些待办事项。 就像我说的那样,非常简单。

To set up the json-server run the following command:

要设置json-server运行以下命令:

npm install --save json-server

Create a file called db.json with the following structure

使用以下结构创建一个名为db.json的文件

Finally, run the following command in the terminal:

最后,在终端中运行以下命令:

json-server --watch db.json --port 8000

This runs a local server on port 8000, and watches the db.json file for any changes. In case anything changes, the server restarts itself. Its a very simple way to mock a server for testing your app.

这将在端口8000上运行本地服务器,并db.json文件是否有任何更改。 万一发生任何更改,服务器将自行重启。 模拟服务器以测试您的应用程序的一种非常简单的方法。

Finally, update your App.js file to reflect your new Todo component, and remove the default JSX from that file.

最后,更新App.js文件以反映新的Todo组件,并从该文件中删除默认的JSX。

Fire up the app (inside of an incognito window) and take a look at what it looks like now. You should see a list of todos and an input box below them with a button to submit. Like I said, very simple UI.

启动该应用程序(在隐身窗口内),然后看一下它的外观。 您应该看到待办事项列表和在其下方的输入框以及一个提交按钮。 就像我说的,非常简单的UI。

Once you’ve got all that set up, let’s figure out a way to make this stuff work offline using Workbox.

完成所有设置后,让我们找到一种使用Workbox使这些内容脱机工作的方法。

Note: While testing service worker functionality in a dev environment, always make sure you do so within a new incognito window each time. It makes testing and debugging much less of a headache because your data is not stored across sessions.

注意:在开发环境中测试服务工作者功能时,请始终确保每次都在新的隐身窗口中进行此操作。 由于您的数据没有跨会话存储,因此它使测试和调试工作变得轻而易举。

使用Workbox实施缓存 (Implementing Caching With Workbox)

Now, if you go ahead and open up the Chrome toolbar, you should see something that looks like the following under the Application tab.

现在,如果继续打开Chrome工具栏,则应在“应用程序”标签下看到类似于以下内容。

Check the offline checkbox and then try to reload your webpage. It will probably fail with an error saying there was no network connection detected. If you look at the network tab, you will see a bunch of failed network requests.

选中离线复选框,然后尝试重新加载您的网页。 它可能会失败,并显示一条错误消息,指出未检测到网络连接。 如果您查看“网络”标签,则会看到一堆失败的网络请求。

The most obvious one that will fail is the request to our json-server to fetch the list of todos. Let’s fix that one first. Open up the custom-service-worker.js file and add in the following code

最明显的失败是请求我们的json-server获取待办事项列表。 让我们先修复一个。 打开custom-service-worker.js文件,并添加以下代码

workbox.routing.registerRoute(  'http://localhost:8000/todos',  workbox.strategies.networkFirst())

This is setting up a caching strategy of networkFirst for any requests made to thehttp://localhost:8000/todos endpoint. The image below gives you a clear explanation of what the networkFirst strategy implies. You always check the network first, and only in case of the network failing do you go to the cache to fetch the resource. This is a typical strategy you might use when querying an API that is likely to provide fresh data.

这将为对http://localhost:8000/todos端点的所有请求设置networkFirst缓存策略。 下图为您清楚地解释了networkFirst策略的含义。 您始终首先检查网络,只有在网络出现故障的情况下,您才进入缓存以获取资源。 这是查询可能提供新鲜数据的API时可能会使用的典型策略。

Now, the app is still not going to load because we are still missing two important pieces. Namely, we are still not caching

现在,该应用程序仍然无法加载,因为我们仍然缺少两个重要部分。 也就是说,我们仍然没有缓存

  1. The JS bundle that is being served by our local dev server.

    我们的本地开发服务器正在提供的JS捆绑包。
  2. The index.html file

    index.html文件

Add the following code to custom-service-worker.js

将以下代码添加到custom-service-worker.js

workbox.routing.registerRoute(
/\.(?:js|css|html)$/,
workbox.strategies.networkFirst(),
)
workbox.routing.registerRoute(
‘http://localhost:3000',
workbox.strategies.networkFirst()
)

If you notice, the first route in the above code snippet is a RegEx object. This is a clean and simple way to target multiple routes with the same strategy. However, if you are targeting a resource that doesn’t follow the same origin policy, make sure to specify the entire route.

如果您注意到,上面的代码片段中的第一条路线是RegEx对象。 这是一种采用同一策略定位多个路由的简洁明了的方法。 但是,如果您定位的资源不遵循相同的原始策略,请确保指定整个路径。

This is, of course, not the ideal way to do things. Ideally, we want static assets like JS bundles, stylesheets and HTML files pre-cached as part of the Webpack build process. We will get to that, but its important to understand that there is no black magic going on. This is all just simple caching.

当然,这不是理想的处理方式。 理想情况下,我们希望将静态资产(如JS包,样式表和HTML文件)预先缓存为Webpack构建过程的一部分。 我们将解决这个问题,但重要的是要了解没有发生任何不可思议的事情。 这只是简单的缓存。

Go ahead and fire up the page again and open up your console. You should see a bunch of logs by Workbox about routing. Go into offline mode, and refresh the page. You should see everything load just like normal. If you open up the workbox logs in the console, you will see Workbox printing out whether the network request failed or succeeded, and workbox’s response to that failure (see screenshot below):

继续并重新启动页面,然后打开控制台。 您应该按Workbox看到一堆有关路由的日志。 进入离线模式,然后刷新页面。 您应该看到一切正常加载。 如果在控制台中打开工作箱日志,您将看到工作箱打印出网络请求是成功还是失败,以及工作箱对该失败的响应(请参见下面的屏幕截图):

使用Workbox实施数据的延迟发布 (Implementing Deferred POSTing Of Data With Workbox)

Alright, next up: how do we POST data back to the server without a network connection?

好吧,接下来:在没有网络连接的情况下,我们如何将数据发布回服务器?

First, let’s set up a way to POST data back online, and make sure it works. Update your addTodo function inside of your Todo component so it looks like the following:

首先,让我们建立一种在线发布POST数据的方法,并确保它可以工作。 更新您的Todo组件内部的addTodo函数,使其如下所示:

All we’ve done is added a callback handler to setState so we can be notified when the state has updated. At this point, we’ve made a POST request to the json-server to update db.json with the new todo.

我们所做的只是向setState添加了一个回调处理程序,以便在状态更新时得到通知。 至此,我们已经向json-server发出了POST请求,以使用新的todo更新db.json

Try submitting a new todo, open up db.json and you should see the new todo added to your array of objects.

尝试提交新的待办事项,打开db.json ,您应该看到新的待办事项已添加到对象数组中。

Now, try doing the exact same thing offline, and you should get a network error for obvious reasons. You will probably get a log statement that says: Failed to fetch.

现在,尝试脱机执行完全相同的操作,由于明显的原因,您应该会遇到网络错误。 您可能会收到一条日志语句,内容为:无法获取。

To solve this, we’re going to make use of something called backgroundSync, the spec for which you can read up on here. The way its supposed to work is that whenever you make a request to a server for a specific resource (in our case a POST request), if no network is detected, Workbox will store this request in indexedDB and keep polling the request for a set period of time. When a network connection is detected, the request will be replayed. If no network connection is established within the pre-defined period of time, the request is discarded.

为了解决这个问题,我们将使用名为backgroundSync的东西,您可以在此处阅读其规范。 它的工作方式是,每当您向服务器请求特定资源(在我们的示例中为POST请求)时,如果未检测到网络,Workbox就会将该请求存储在indexedDB中并继续轮询该请求以获取集合一段的时间。 当检测到网络连接时,将重播该请求。 如果在预定的时间内未建立任何网络连接,则该请求将被丢弃。

The backgroundSync API uses something called SyncManager under the hood. You can read about it in the MDN docs here. Unfortunately, as you can see, SyncManager is not on the standards track and Chrome is the only browser that has a fully implemented spec. What this means is that Chrome is the only browser where this is guaranteed to work reliably.

backgroundSync API在后台使用了称为SyncManager的东西。 您可以在MDN文档中阅读有关此内容的信息 。 不幸的是,如您所见,SyncManager不在标准轨道上,Chrome是唯一具有完全实现的规范的浏览器。 这意味着Chrome是唯一可以保证其可靠运行的浏览器。

We need to add some code to custom-service-worker.js to get the backgroundSync stuff working for us. Add the following code to the file:

我们需要向custom-service-worker.js添加一些代码,以使backgroundSync内容对我们custom-service-worker.js 。 将以下代码添加到文件中:

We are making use of a background sync plugin that Workbox provides us with. The first parameter you provide to the constructor is the name of the queue you want Workbox to create when storing failed requests. The second parameter is an options object, where we are defining the maximum amount of time to attempt to replay requests within.

我们正在使用Workbox为我们提供的后台同步插件。 您提供给构造函数的第一个参数是您希望Workbox在存储失败请求时创建的队列的名称。 第二个参数是一个options对象,其中我们定义了尝试重播请求的最大时间。

Finally, we register a new route with the POST method, and set up the strategy we want to use for caching. This is very similar to what we have already done with the exception of defining the type of request being made, and also having a plugin defined for our strategy.

最后,我们使用POST方法注册新路由,并设置我们要用于缓存的策略。 这与我们已经完成的工作非常相似,只是定义了要发出的请求的类型,并且还为我们的策略定义了一个插件。

Now, try running through the same scenario of submitting a todo without any network connection and observe what happens in the log. You will get a log that looks like the following screenshot.

现在,尝试运行在没有任何网络连接的情况下提交待办事项的相同场景,并观察日志中发生了什么。 您将获得类似于以下屏幕截图的日志。

You can look at the request that has been added by looking for indexedDB under the application tab in the Chrome DevTools window. Open up the listed subdirectories under the indexedDB dropdown menu, and you should see the request stored, waiting to be replayed.

您可以通过在Chrome DevTools窗口的应用程序标签下查找indexedDB来查看已添加的请求。 在indexedDB下拉菜单下打开列出的子目录,您应该看到已存储请求,等待重播。

Switch off the offline option in the DevTools window, and you should see a new Workbox log popup almost immediately. It will look like the following:

关闭DevTools窗口中的离线选项,您应该几乎立即看到一个新的Workbox日志弹出窗口。 它将如下所示:

The image above involves Workbox replaying the failed request the moment it receives a sync request, and giving you the confirmation that your request has been successful. If you look at db.json now, you will notice that the new todo has been added to the file.

上面的图像涉及Workbox在收到同步请求后立即重播失败的请求,并向您确认请求已成功。 如果现在查看db.json ,您会注意到新的待办事项已添加到文件中。

Well, there we go. We have a way to replay failed requests through a service worker now.

好吧,我们去。 我们现在有一种方法可以通过服务工作者重放失败的请求。

What we need to do next is to integrate a Webpack plugin so Workbox can cache static assets as part of the build process. This will get rid of the need to explicitly have a route to cache static assets inside of our Service Worker file.

接下来,我们需要集成一个Webpack插件,以便Workbox可以在构建过程中缓存静态资产。 这样就无需显式地具有在我们的Service Worker文件中缓存静态资产的路由。

缓存静态资产 (Precaching Static Assets)

This is going to be the final step. In this section, we are going to make the changes to CRA’s build process to force it to generate the Service Worker file using Workbox instead of sw-precache.

这将是最后一步。 在本节中,我们将对CRA的构建过程进行更改,以强制其使用Workbox而不是sw-precache生成Service Worker文件。

First up, install the following packages: workbox-webpack-plugin and path.

首先,安装以下软件包: workbox-webpack-pluginpath

Open up the package.json file and edit the build script to run with react-app-rewired instead of react-scripts the same way we did for the start script.

开拓package.json文件和编辑构建脚本与运行react-app-rewired ,而不是react-scripts我们为启动脚本的方式相同。

Finally, open up the config-overrides.js file and edit it to look like the following:

最后,打开config-overrides.js文件并将其编辑为如下所示:

There’s a couple of things we’re doing in this file.

在此文件中,我们正在做几件事。

First, we check to see if it’s a production build. If it is, we create a Workbox config object and provide it with the path of our custom SW, and also the path of the output SW we want.

首先,我们检查它是否是生产版本。 如果是这样,我们将创建一个Workbox配置对象,并为其提供定制SW的路径以及所需的输出SW的路径。

We also provide an option called importWorkboxFrom and set it to disabled.

我们还提供了一个名为importWorkboxFrom的选项,并将其设置为disabled

This is an option specifying that we don’t want Workbox imported from anywhere, since we’re directly requesting it from a CDN in our SW script.

这是一个选项,用于指定我们不希望从任何地方导入Workbox,因为我们直接从SW脚本中的CDN请求它。

Finally, we have a function that is called removeSWPrecachePlugin . All this does is loop over the plugins listed in the Webpack config, find the correct one, and return the index so we can remove it.

最后,我们有一个称为removeSWPrecachePlugin的函数。 所有这些操作就是遍历Webpack配置中列出的插件,找到正确的插件,然后返回索引,以便我们将其删除。

Now, go ahead and run the build for the app, and open up the SW file generated in the build folder. In my case, this SW file has the name custom-service-worker.js

现在,继续运行该应用程序的构建,然后打开在构建文件夹中生成的SW文件。 就我而言,此SW文件的名称为custom-service-worker.js

You will notice a new importScripts call at the top of the file, which seems to be requesting a precache manifest file. This file is stored in the build folder, and if you open it up, you should see the list of all static assets being cached by Workbox.

您会在文件顶部注意到一个新的importScripts调用,这似乎是在请求预缓存清单文件。 该文件存储在build文件夹中,如果打开它,则应该看到Workbox缓存的所有静态资产的列表。

结论 (Conclusion)

So, we’ve accomplished the following goals:

因此,我们实现了以下目标:

  1. Configure the CRA build to use react-app-rewired

    配置CRA构建以使用react-app-rewired

  2. Use react-app-rewired to customise the build to use Workbox to generate a Service Worker — We accomplished this using workbox-webpack-plugin. The build process will now automatically cache all static assets.

    使用react-app-rewired定制生成以使用Workbox生成服务工作器的构建-我们使用workbox-webpack-plugin.完成了此工作workbox-webpack-plugin. 现在,构建过程将自动缓存所有静态资产。

  3. Build a very simple todo app

    构建一个非常简单的待办应用
  4. Implement offline functionality for the todo app using Workbox.

    使用Workbox为待办事项应用程序实现离线功能。

    The offline functionality we will be targeting:

    我们将针对的离线功能:

    a) Cache retrieved assets so they can be served offline

    a)缓存检索到的资产,以便可以离线提供它们

    b) Allow POSTing of data offline

    b)允许离线发布数据

Here is the link to the repo which has a working version of the app. You can clone that and have a play with it.

这是具有该应用程序工作版本的回购链接 。 您可以克隆该文件并进行操作。

Follow me on twitter here. Follow me on GitHub here

这里在Twitter上关注我。 跟我在GitHub上这里

翻译自: https://www.freecodecamp.org/news/how-to-build-a-custom-pwa-with-workbox-in-create-react-app-be580686cf73/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值