在上一篇文章记一次基于react、cra二、typescript的pwa项目由开发到部署(一)中,咱们了解到了create-react-app 给咱们提供了哪些pwa支持,也了解到了有哪些不足。虽然create-react-app会帮咱们自动生成一个service-worker.js 去缓存咱们的app shell,可是并无提供让开发者定制service worker的方法,除非咱们eject项目,这篇文章继续往下讲,把在这个项目中学到的东西分享给你们。html
这是一个移动端的pwa应用,使用react,typescript,react-redux,react-router,workbox 基于create-react-app 开发。能够添加到主屏幕,能够断网条件下正常打开和访问数据。项目地址:browseExpbyReactvue
typescript是JavaScript的超集,一方面在typescript中咱们可使用最新的特性,另外一方面typescript给咱们带来了类型系统,可让咱们写出健壮的代码,避免一些潜在的运行时错误。在create-react-app中使用typescript,官网推荐咱们使用的是create-react-app的ts版本,他会帮你配置好typescript的相关配置,并使用react-script-ts代替react-script来驱动项目。可是这个版本的更新会稍稍滞后于原版,并且也不利于咱们扩展脚手架的配置,因此这里不推荐使用。咱们使用 react-app-rewired 来进行配置。react
感谢读者提醒:October 29, 2018发布的 v2.1.0 利用bebal7 添加了对typescript的支持,如今咱们只要运行 create-react-app my-app --typescript 就可以的到typescript的支持了,能够关注如下网址关注create-react-app 的更新变更create-react-app releases,可是该项目中仍是须要用到react-app-rewired,须要借助它去修改咱们默认的Workbox webpack plugin配置。webpack
在create-react-app中修改默认配置有两种经常使用的方法,nginx
v2.1.0版本起,再也不须要 react-app-rewire-typescript 去配置typescript的支持。只要运行 create-react-app my-app --typescript 就可以的到typescript的支持。若是使用cra版本为v2.1.0及日后版本,能够忽略本文关于typescript的配置。git
这里到了本文的重点:如何在create-react-app中定制本身的service-worker.js。目前的cra引用了Workbox webpack plugin 代替了先前的 sw-precache-webpack-plugin。咱们能够借助 react-app-rewired 去改写默认的Workbox webpack plugin 配置。主要步骤:github
首先在 config.overrides.js 中配置,替换默认的workbox-webpack-plugin配置:web
/* 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的配置,把它删掉,而后用本身的配置替代它。typescript
这里解释一下 removePreWorkboxWebpackPluginConfig 这个函数。咱们能够本身用create-react-app新建一个无用的项目,而后eject它,那么咱们能够在暴露出来的config文件夹下的 webpack.config.prod.js 中看到关于 workbox-webpack-plugin 的配置shell
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可让咱们的web app添加到桌面,再create-react-app中配置manifest很是简单,直接再public目录下的manifest.json配置就能够了,关于什么么配置项,能够到这里谷歌官网教程查看,另外manifest.json的配置不会立刻生效,须要在https协议下,屡次进入该网页的时候才会弹出添加到桌面的提示。
到这里咱们能够在create-react-app生成的脚手架中定制本身的pwa配置了,在下一篇文章中,我会继续讲解:
感兴趣的同窗能够扫描下面二维码体验项目:
note:
- 建议用uc浏览器打开,由于uc浏览器对pwa的支持较好。
- "添加到桌面的提示" 须要短期屡次进入web app 才会触发
项目地址:browseExpByReact 若是感兴趣,能够对比着基于vue的实现来看: browseExpByVue