Реагирует на пользовательский рабочий сервис, содержащий оператор require() в рабочем окне

#reactjs #workbox

#reactjs #рабочее окно

Вопрос:

Я хочу создать пользовательский рабочий сервис в react, чтобы прослушивать сетевые события (выборка, активация и т. Д.). Некоторые события создаются IPFS, таким образом, пользовательский sw.
Я хочу использовать workbox, поскольку он, похоже, поддерживается сейчас. (sw-precache, похоже, больше не поддерживается). Поскольку пользовательский sw содержит

 const IPFS = require('ipfs')
  

Мне нужен browserify. Затем я импортирую этот скрипт, проверенный браузером, в react sw. Похоже, что мой пользовательский sw не используется в react sw.

Вот как выглядит мой файл package.json

 {
  "name": "custom-react-sw",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "@zippie/ipfs-postmsg-proxy": "^3.1.6",
    "ipfs": "^0.50.2",
    "ipfs-postmsg-proxy": "^3.1.1",
    "orbit-db": "^0.26.0",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "3.4.4",
    "workbox": "0.0.0",
    "workbox-cli": "^5.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build amp;amp; npm run bundle-sw-dependencies amp;amp; workbox generateSW workbox-config.js",
    "bundle-sw-dependencies": "browserify src/custom-service-worker.js -o build/service-worker-ipfs.js",
    "minify": "terser build/service-worker-ipfs.js --output build/service-worker-ipfs.min.js amp;amp; npm run cleanup",
    "cleanup": "rm build/service-worker-ipfs.js",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

  

Пользовательский рабочий сервис:

 'use strict'
const IPFS = require('ipfs')
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js')


if (workbox) {
  console.log(`Yay! Workbox is loaded 🎉`);
  self.addEventListener('fetch', (event) => {
    console.log(event)
  })


} else {
  console.log(`Boo! Workbox didn't load 😬`);
}

self.addEventListener('fetch', e => {
  console.log(e)
  console.log(`intercepting ${e.request.method} to ${e.request.url}`)
})
  

Затем рабочий сервис (созданный react)

 // This optional code is used to register a service worker.
// register() is not called by default.

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.



const isLocalhost = Boolean(
  window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.0/8 are considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export function register(config) {
  if (process.env.NODE_ENV === 'production' amp;amp; 'serviceWorker' in navigator) {
    // The URL constructor is available in all browsers that support SW.
    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
    if (publicUrl.origin !== window.location.origin) {
      // Our service worker won't work if PUBLIC_URL is on a different origin
      // from what our page is served on. This might happen if a CDN is used to
      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
      return;
    }

    window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

      if (isLocalhost) {
        // This is running on localhost. Let's check if a service worker still exists or not.
        checkValidServiceWorker(swUrl, config);

        // Add some additional logging to localhost, pointing developers to the
        // service worker/PWA documentation.
        navigator.serviceWorker.ready.then(() => {
          console.log(
            'This web app is being served cache-first by a service '  
              'worker. To learn more, visit '
          );
        });
      } else {
        // Is not localhost. Just register service worker
        registerValidSW(swUrl, config);
      }
    });
  }
}

function registerValidSW(swUrl, config) {
  navigator.serviceWorker
    .register(swUrl)
    .then(registration => {
      console.log('Service worker registered')

      registration.onupdatefound = () => {
        const installingWorker = registration.installing;
        if (installingWorker == null) {
          return;
        }
        installingWorker.onstatechange = () => {
          if (installingWorker.state === 'installed') {
            if (navigator.serviceWorker.controller) {
              // At this point, the updated precached content has been fetched,
              // but the previous service worker will still serve the older
              // content until all client tabs are closed.
              console.log(
                'New content is available and will be used when all '  
                  'tabs for this page are closed. See '
              );

              // Execute callback
              if (config amp;amp; config.onUpdate) {
                config.onUpdate(registration);
              }
            } else {
              // At this point, everything has been precached.
              // It's the perfect time to display a
              // "Content is cached for offline use." message.
              console.log('Content is cached for offline use.');

              // Execute callback
              if (config amp;amp; config.onSuccess) {
                config.onSuccess(registration);
              }
            }
          }
        };
      };
    })
    .catch(error => {
      console.error('Error during service worker registration:', error);
    });

}


function checkValidServiceWorker(swUrl, config) {
  // Check if the service worker can be found. If it can't reload the page.
  fetch(swUrl, {
    headers: { 'Service-Worker': 'script' },
  })
    .then(response => {
      // Ensure service worker exists, and that we really are getting a JS file.
      const contentType = response.headers.get('content-type');
      if (
        response.status === 404 ||
        (contentType != null amp;amp; contentType.indexOf('javascript') === -1)
      ) {
        // No service worker found. Probably a different app. Reload the page.
        navigator.serviceWorker.ready.then(registration => {
          registration.unregister().then(() => {
            window.location.reload();
          });
        });
      } else {
        // Service worker found. Proceed as normal.
        registerValidSW(swUrl, config);
      }
    })
    .catch(() => {
      console.log(
        'No internet connection found. App is running in offline mode.'
      );
    });
}

export function unregister() {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.ready
      .then(registration => {
        registration.unregister();
      })
      .catch(error => {
        console.error(error.message);
      });
  }
}

  

App.js досье. Событие выборки происходит оттуда

 import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';

const App = () => {
  const [name,setName] = useState("")
  const [userInfo, setUserInfo] = useState({})
  const handleChange = e => setName(e.target.value);
  const handleClick = () => {
    console.log(`fetching information for ${name}`)
    fetch(`https://api.github.com/users/${name}`)
        .then((res => res.json()))
        .then(data => {setUserInfo(data)
                        console.log(data)})
  }

  return (
    <div className="App">
    <h1> Git user infor </h1>
    <div className={'form'}>
      <input type='text' value={name} onChange={handleChange} className={'text-box'}/>
      <input type='button' value={'Submit'} onClick={handleClick} className={'button'}/>
    </div>

    {Object.keys(userInfo).length > 0 amp;amp; <div className={'table'}>
      <table>
        <tbody>
        {Object.entries(userInfo).map(([k,v], i) => {
          return <tr key={i}>
            <td>{k}</td>
            <td>{v}</td>
          </tr>
        })}
        </tbody>
      </table>
    </div>}
  </div>
  );
}

export default App;

  

Я получаю Yay! Workbox is loaded , но, похоже, он не переходит в выборку.

РЕДАКТИРОВАТЬ Итак, если я удалю `const IPFS = require (‘ipfs’), тогда все будет работать нормально. Как я могу включить это в свой csw?

Ответ №1:

Паскаль,

Учитывая так много движущихся частей, трудно сказать, что именно идет не так. Если бы объединение IPFS было проблемой, я бы не ожидал увидеть Yay! Workbox is loaded , но опять же, это зависит от того, как выглядит этот результирующий сценарий, в частности, например. ошибка после console.log может привести к сбою установки service worker и объяснит, почему вы видите зарегистрированное сообщение, но ничего не получаете.

Также стоит учитывать, что работники службы имеют сложные жизненные циклы:

  • Первая загрузка не будет делегировать запросы на выборку рабочему сервису, поскольку он не будет активирован по умолчанию до следующей загрузки.
  • Измененный сценарий service worker может не быть обнаружен при обновлении, и даже если это так, он все равно не будет активирован (если sw не выбирает-into, чего я не вижу в вашем коде).

Это хорошая статья, в которой все это подробно описано и предлагается несколько советов о том, как тестировать и справляться со сложными жизненными циклами с помощью devtools: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle

Теперь, помимо части service worker, также стоит учитывать, что запуск IPFS в самом Service Worker может быть не идеальным, потому что:

Браузеры деактивируют SW, как только он заканчивает отвечать на запрос (иногда даже раньше). Который довольно хорошо работает для типичных серверных клиентских архитектур, но не так хорошо для архитектур P2P, потому что узлам необходимо обнаруживать другие одноранговые узлы и поддерживать работоспособные соединения, чтобы иметь возможность эффективно находить и извлекать содержимое.

Тем не менее, недавно стало возможным запускать JS-IPFS в SharedWorker и использовать его API в других потоках. Что также создает возможность использовать общий узел IPFS от service worker. Если это звучит в соответствии с вашими целями, я бы предложил подписаться на соответствующие вопросы.