在上一篇文章记一次基于 react,cra2,typescript 的 pwa 项目由开发到部署 (一) 中, 我们了解到了 create-react-App 给我们提供了哪些 pwa 支持, 也了解到了有哪些不足. 虽然 create-react-App 会帮我们自动生成一个 service-worker.JS 去缓存我们的 App shell, 但是并没有提供让开发者定制 service worker 的方法, 除非我们 eject 项目, 这篇文章继续往下讲, 把在这个项目中学到的东西分享给大家.
项目回顾
这是一个移动端的 pwa 应用, 使用 react,typescript,react-redux,react-router,workbox 基于 create-react-App 开发. 可以添加到主屏幕, 可以断网条件下正常打开和访问数据. 项目地址: https://github.com/HolyZheng/browseExpbyReact
使用 typescript
typescript 是 JavaScript 的超级, 一方面在 typescript 中我们可以使用最新的特性, 另一方面 typescript 给我们带来了类型系统, 可以让我们写出健壮的代码, 避免一些潜在的运行时错误. 在 create-react-App 中使用 typescript, 官网推荐我们使用的是 create-react-App 的 ts 版本, 他会帮你配置好 typescript 的相关配置, 并使用 react-script-ts 代替 react-script 来驱动项目. 但是这个版本的更新会稍稍滞后于原版, 而且也不利于我们扩展脚手架的配置, 所以这里不推荐使用. 我们使用 https://github.com/timarney/react-app-rewired 来进行配置.
react-App-rewired
在 create-react-App 中修改默认配置有两种常用的方法,
一种是 eject 项目, eject 会把我们的脚手架中的配置暴露出来, 然后我们就可以去修改了, 但是这是一个不可逆的过程, 而且讲配置暴露出来也是一个不优雅的做法, 所以不推荐.
第二种就是利用 https://github.com/timarney/react-app-rewired 去修改我们的配置, 他可以让我们在不 eject 项目的前提下修改我们的配置. 比如配置 typescript, 我们可以找到对应的插件 进行配置. 具体可参考本项目
利用 workbox 定制自己的 service worker
这里到了本文的重点: 如何在 create-react-App 中定制自己的 service-worker.JS. 目前的 cra 引用了 Workbox webpack plugin 代替了先前的 sw-precache-webpack-plugin. 我们可以借助 react-App-rewired 去改写默认的 Workbox webpack plugin 配置. 主要步骤:
在 react-App-rewired 的配置文件 config.overrides.JS 中修改 Workbox webpack plugin 的配置
在 public 文件目录下建立自己的 service-worker 配置文件
首先在 config.overrides.JS 中配置, 替换默认的 workbox-webpack-plugin 配置:
- /* config-overrides.JS */
- // typescript 的配置插件
- const rewireTypescript = require('react-app-rewire-typescript');
- const workboxPlugin = require('workbox-webpack-plugin')
- const path = require('path')
- module.exports = {
- webpack: function (config, env) {
- // typescript 的配置插件
- config = rewireTypescript(config, env);
- if (env === 'production') {
- // 在'production' 模式下加入自己的配置
- const workboxConfigProd = {
- swSrc: path.join(__dirname, 'public', 'cus-service-worker.js'),
- swDest: 'cus-service-worker.js',
- importWorkboxFrom: 'disabled'
- }
- // 删除默认的 WorkboxWebpackPlugin 配置
- config = removePreWorkboxWebpackPluginConfig(config)
- // 加入我们的配置
- config.plugins.push(new workboxPlugin.InjectManifest(workboxConfigProd))
- }
- return config
- }
- }
- // 此函数用来找出 默认配置中的 WorkboxWebpackPlugin, 并把它删除
- function removePreWorkboxWebpackPluginConfig (config) {
- const preWorkboxPluginIndex = config.plugins.findIndex((element) => {
- return Object.getPrototypeOf(element).constructor.name === 'GenerateSW'
- })
- if (preWorkboxPluginIndex !== -1) {
- config.plugins.splice(preWorkboxPluginIndex, 1)
- }
- return config
- }
这部分的配置大概意思就是, 当环境为生成环境时, 找出 webpack 中关于 workbox-webpack-plugin 的配置, 把它删掉, 然后用自己的配置替代它.
这里解释一下 removePreWorkboxWebpackPluginConfig 这个函数. 我们可以自己用 create-react-App 新建一个无用的项目, 然后 eject 它, 那么我们可以在暴露出来的 config 文件夹下的 webpack.config.prod.JS 中看到关于 workbox-webpack-plugin 的配置
- new WorkboxWebpackPlugin.GenerateSW({
- clientsClaim: true,
- exclude: [/\.map$/, /asset-manifest\.JSON$/],
- importWorkboxFrom: 'cdn',
- navigateFallback: publicUrl + '/index.html',
- navigateFallbackBlacklist: [
- // Exclude URLs starting with /_, as they're likely an API call
- new RegExp('^/_'),
- // Exclude URLs containing a dot, as they're likely a resource in
- // public/ and not a SPA route
- new RegExp('/[^/]+\\.[^/]+$'),
- ],
- }),
所以我们可以通过下面这段代码找到这段配置的位置:
- // 对 plugins 数组调用 findIndex 方法, 找到构造函数的 name 属性为'GenerateSW'的成员
- const preWorkboxPluginIndex = config.plugins.findIndex((element) => {
- return Object.getPrototypeOf(element).constructor.name === 'GenerateSW'
- })
- // 删除这个成员
- if (preWorkboxPluginIndex !== -1) {
- config.plugins.splice(preWorkboxPluginIndex, 1)
- }
替换掉 workbox-webpack-plugin 的配置后, 根据自己的配置在 public 目录下新建 cus-service-worker.JS 文件, 这个文件会代替默认生成的 service-worker.JS 文件, 我们就可以通过配置 cus-service-worker.JS 来定制自己的 pwa 配置了, 而且 cus-service-worker.JS 里的内容也是有讲究的, 以本项目为例:
- // 引入 workbox 全局变量
- importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js');
- if (workbox) {
- console.log(`Yay! Workbox is loaded `);
- } else {
- console.log(`Boo! Workbox didn't load `);
- }
- // set the prefix and suffix of our sw's name
- workbox.core.setCacheNameDetails({
- prefix: 'browse-exp',
- suffix: 'v1.0.0',
- });
- // have our sw update and control a Web page as soon as possible.
- workbox.skipWaiting();
- workbox.clientsClaim();
- // 将静态资源进行预缓存
- self.__precacheManifest = [].concat(self.__precacheManifest || []);
- workbox.precaching.suppressWarnings();
- workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
- // 定制自己的需求
- // cache our data, and use networkFirst strategy.
- workbox.routing.registerRoute(
- new RegExp('.*experiments\?.*'),
- workbox.strategies.networkFirst()
- );
- workbox.routing.registerRoute(
- new RegExp('.*experiments/\\d'),
- workbox.strategies.networkFirst()
- )
- workbox.routing.registerRoute(
- new RegExp('.*experiment_types.*'),
- workbox.strategies.networkFirst()
- )
首先通过 importScripts 引入 workbox 全局变量. 在打包的时候, 脚手架会为我们生成一个 precache-manifest 列表, 里面会列举一系列的静态文件, 我们可以通过 self.__precacheManifest 拿到这个列表, 所以我们需要通过一下语句预缓存这些静态资源:
- self.__precacheManifest = [].concat(self.__precacheManifest || []);
- workbox.precaching.suppressWarnings();
- workbox.precaching.precacheAndRoute(self.__precacheManifest, {
- });
然后就是为了尽快的让我们的 service worker 控制页面, 我们可以在开头加入一下语句:
- // 跳过等待
- workbox.skipWaiting();
- // 控制客户端
- workbox.clientsClaim();
剩下的部分自己就可以按自己的需求进行发挥了, 像要什么功能就配置什么功能, 这里的话我为自己获取数据的路由进行了缓存, 采用的是 networkFirst 策略, 什么是 networkFirst 策略呢? 就是首先会进行网络请求, 如果失败的话再使用缓存中的数据.
当我们打包项目的时候, 就会发现再 build 文件下, 会生成一个 cus-service-worker.JS 文件, 并且再开头多了一句:
importScripts("/precache-manifest.cd8115bc0ff644d6d74bec08ffcbdeb4.js");
这就是我们可以通过 self.__precacheManifest 拿到预缓存列表的原因.
到目前为止: 我们已经可以定制自己的 service-worker.JS 了.
manifest.JSON
manifest.JSON 可以让我们的 Web App 添加到桌面, 再 create-react-App 中配置 manifest 非常简单, 直接再 public 目录下的 manifest.JSON 配置就可以了, 关于什么么配置项, 可以到这里谷歌官网教程查看, 另外 manifest.JSON 的配置不会马上生效, 需要在 https 协议下, 多次进入该网页的时候才会弹出添加到桌面的提示.
总结:
利用 react-App-rewired 改写我们的配置
在 config.overrides.JS 中替换默认的 WorkboxWebpackPlugin 的配置
在 public 目录下编写自己的 pwa 配置
到这里我们可以在 create-react-App 生成的脚手架中定制自己的 pwa 配置了, 在下一篇文章中, 我会继续讲解:
如何部署将该项目部署到 nginx 服务器上.
为它配置证书, 让它运行在 https 协议上.
体验该 pwa 项目.
感兴趣的同学可以扫描下面二维码体验项目:
note:
建议用 uc 浏览器打开, 因为 uc 浏览器对 pwa 的支持较好.
"添加到桌面的提示" 需要短时间多次进入 Web App 才会触发
项目地址: browseExpByReact https://github.com/HolyZheng/browseExpbyReact 如果感兴趣, 可以对比着基于 vue 的实现来看: browseExpByVue https://github.com/HolyZheng/BrowseExp
来源: https://juejin.im/post/5bd827716fb9a05d212ee743