The foreword 0.

We all know that PWA is a new technology. It can run normally offline and turn on in seconds, relying on caching. I migrated the original game to React, then to Webpack + React, and finally to PWA. I won’t go into details, but let’s get started.

1.webpack

There are many webpack walkthrough, without further ado, a brief introduction to some key points. Remember a few points: entry, output, plug-in plugins, module loader. Your next complete project should contain at least this.

There is also a path module, specifically read the path, do all the configuration, the path first do well:

// This is how we usually do it
var path = require("path");
var ROOT_PATH = path.resolve(__dirname);// The current main entry directory
var SRC_PATH = path.resolve(ROOT_PATH,"src");// SRC, the code you wrote is here
var DIST_PATH = path.resolve(ROOT_PATH,"dist");// Package the result
var COMP_PATH = path.resolve(SRC_PATH,"component");// React has both components

// Then our configuration inside
var config = {
    mode:'development'.entry: path.resolve(__dirname, './src/index.js'),// Webpack turns the main entry HTML into JS, and then injects the HTML
    output:{
        path:DIST_PATH,
        filename:"bundle.js"}},Copy the code

Module loaders, normally we do not use the preprocessor, continue to add configuration in config, this way basically meet the needs

    module: {rules:[
            {
                test:/\.(es6|js)$/.// Take es6 into account
                use:[
                    {
                        loader:"babel-loader",}],exclude:/node_modules/   // Do not take nodemodules into account
            },
            {
                test:/\.(css)$/.use:[
                    {
                        loader:"style-loader"
                    },
                    {
                        loader:"css-loader"}].exclude:/node_modules/
            },
            {
                test:/\.(png|jpeg|jpg|gif)$/.use:[
                    {
                        loader:"url-loader",}],exclude:/node_modules/}}]Copy the code

For plugins, htmlWebpackPlugin and hot updates are usually enough

    plugins:[
        new webpack.HotModuleReplacementPlugin(),
        new htmlWebpackPlugin({
             title: 'game'.template: path.resolve(__dirname, './index.html'),
             //bunld.js will be injected into it
             inject: true
        }),
        new OfflinePlugin() // This is used by PWA, which will be covered later
    ]
Copy the code

There is also a server:

var server = new WebpackDevServer(webpack(config), {
    contentBase: path.resolve(__dirname, './dist'), // The local server is provided as a root folder by default, specifying the folder here
    historyApiFallback: true.// This is the history route. If set to true, all jumps will point to index.html
    port: 9090./ / the default 8080
    publicPath: "/".// The directory where the page is loaded by the local server
    hot: true./ / hot update
    inline: true.// Refresh in real time
    historyApiFallback: true / / don't jump
});
server.listen(9090.'localhost'.function (err) {
    if (err) throw err
})
Copy the code

Oh, and list the require manifest and package.json:

var webpack = require("webpack");
var path = require("path");
var htmlWebpackPlugin = require("html-webpack-plugin");
var webpackDelPlugin = require("webpack-del-plugin");
var WebpackDevServer = require('webpack-dev-server');
var ROOT_PATH = path.resolve(__dirname);
var SRC_PATH = path.resolve(ROOT_PATH,"src");
var DIST_PATH = path.resolve(ROOT_PATH,"dist");
var TEM_PATH = path.resolve(SRC_PATH,"component");
var  OfflinePlugin = require('offline-plugin')


//package.json
{
  "name": "pwawebpack"."version": "1.0.0"."main": "index.js"."license": "MIT"."dependencies": {
    "jquery": "^ 3.3.1"."react-scripts": 1.1.1 ""
  },
  "devDependencies": {
    "babel-core": "^ 6.26.3"."babel-loader": "^" 7.1.4."babel-plugin-react-transform": "^ 3.0.0"."babel-preset-es2015": "^ 6.24.1"."babel-preset-react": "^ 6.24.1"."babel-preset-react-hmre": "^ 1.1.1"."css-loader": "^ 0.28.11"."file-loader": "^ 1.1.11"."html-webpack-plugin": "^ 3.2.0"."offline-plugin": "^ 5.0.3." "."react": "^ 16.3.2"."react-dom": "^ 16.3.2"."react-transform-hmr": "^" 1.0.4."style-loader": "^ 0.21.0"."url-loader": "^" 1.0.1."webpack": "^ 4.8.3"."webpack-cli": "^ 2.1.3"."webpack-del-plugin": "Hundreds"."webpack-dev-server": "^ 3.1.4." "."webpack-notifier": "^ 1.6.0." "
  },
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config webpack.config.js"}}Copy the code

In order to quickly see the effects of pWA +webpack, we will not write ESLint and test

2.pwa

Let’s take the examples from Baidu. A normal PWA consists of index.html, a CSS, a manifest.json, and a sw.js. We’re going to start a PWA, which is a must. Actually, doesn’t it look a bit like an extension to Chrome? Have you ever tried writing your own Chrome add-ons for Google, such as adblocking, personal tools, favorites to certain websites, etc. After all, it looks a bit like a family.

html:

<head>
  <title>PWA</title>
  <meta name="viewport" content="width=device-width, user-scalable=no" />
  <link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
  <h1>1</h1>
</body>
Copy the code

Manifest.json: This is basically the same browser extension you wrote yourself, with name, style, logo configuration

{
  "name": "PWA"."short_name": "p"."display": "standalone"."start_url": "/"."theme_color": "#0000ff"."background_color": "#00ff00"."icons": [{"src": "logo.png"."sizes": "256x256"."type": "image/png"}}]Copy the code

The lifecycle of the software includes Parsed, Installed, Activated, and Redundant failover. If it fails, it jumps to the Redundant phase. Then we listen for these events and see what happens.

var cacheStorageKey = 'v1'

var cacheList = [
  '/'."index.html"."main.css"."logo.png"
]

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheStorageKey)
    .then(cache= > cache.addAll(cacheList))
    .then((a)= > self.skipWaiting())
  )
})

self.addEventListener('fetch'.function(e) {
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if(response ! =null) {
        return response
      }
      return fetch(e.request.url)
    })
  )
})

self.addEventListener('activate'.function(e) {
  e.waitUntil(
    Promise.all(
      caches.keys().then(cacheNames= > {
        return cacheNames.filter(name= >name ! == cacheStorageKey ).map(name= >caches.delete(name))
      })
    ).then((a)= > {
      return self.clients.claim()
    })
  )
})
Copy the code

Note that pWA requires HTTPS or localhost, because it can pull all your local files, which can do other things, so it has to be safe to run. Also, is it found that the HTML, JS files changed, empty the cache does not update it? In fact, you can change the sw, manifest application cache is also, change the version number, or add a space on the line.

3. Webpack-based PWA

Documentation is available on the official website.

We can run without configuration, but there are some things that need to be paid attention to in the configuration and can not be arbitrarily changed, see the documentation. Common Settings are: Caches (default to all cache, or set yourself), externals (array form, means other resources such as CDN), excludes (array form, except what can’t be cached), autoUpdate (how long does it take to update, defaults to one hour)

We use offline-plugin. We just need to introduce it directly in the plugin:

 plugins: [
    / /... Other plug-ins
    new OfflinePlugin()
  ]
Copy the code

Then add to our entry file index.js:

import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
Copy the code

Try offline mode for Google Chrome and you’ll find it still works: