在上一篇文章記一次基於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 開發。能夠添加到主屏幕,能夠斷網條件下正常打開和訪問數據。項目地址:browseExpbyReact vue
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
在create-react-app中修改默認配置有兩種經常使用的方法,webpack
這裏到了本文的重點:如何在create-react-app中定製本身的service-worker.js。目前的cra引用了Workbox webpack plugin 代替了先前的 sw-precache-webpack-plugin。咱們能夠藉助 react-app-rewired
去改寫默認的Workbox webpack plugin 配置。主要步驟:nginx
首先在 config.overrides.js 中配置,替換默認的workbox-webpack-plugin配置:git
/* 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的配置,把它刪掉,而後用本身的配置替代它。github
這裏解釋一下 removePreWorkboxWebpackPluginConfig 這個函數。咱們能夠本身用create-react-app新建一個無用的項目,而後eject它,那麼咱們能夠在暴露出來的config文件夾下的 webpack.config.prod.js 中看到關於 workbox-webpack-plugin 的配置web
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('/[^/]+\\.[^/]+$'), ], }),
因此咱們能夠經過下面這段代碼找到這段配置的位置:typescript
// 對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 裏的內容也是有講究的,以本項目爲例:shell
// 引入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