[訳]先端オフラインガイド(以下)


[翻訳]先端オフラインガイド(上)]https://juejin.im/post/5c0788...
译文リンク:The offine cookbook著者:Jake Archibald
キャッシュの耐久化
あなたのサイトに必要な操作を実行するための一定量の利用可能な空間を提供します.この利用可能な空間は、サイト内のすべての記憶間で共有できます.Local Strage、IndexedDB、Filesystem、もちろんCachesも含まれます.
取得できる空間容量は必ずしも限られていません.また、デバイスと格納条件の違いによっても異なります.取得した空間容量は以下のコードで確認できます.
navigator.storageQuota.queryInfo("temporary").then((info) => {
  console.log(info.quota);
  // Result: 
  console.log(info.usage);
  // Result: 
});
しかし、すべてのブラウザと同じように、デバイスが格納圧力に直面すると、ブラウザはいつでもこれらの保存内容を破棄します.しかし、残念なことに、ブラウザはあなたの大切にしている映画を区別できません.興味のないゲームとの違いは何ですか?
この問題を解決するために、request Persistent APIを使用することを提案します.
//       
navigator.storage.requestPersistent().then((granted) => {
  if (granted) {
    //   ,        
  }
});
もちろん、ユーザーに権限を与える必要があります.このプロセスに参加させる必要があります.ユーザーは削除をコントロールすることができると期待しています.ユーザーが持っているデバイスが格納圧力に直面しています.重要ではないデータをクリアしても問題が解決されていません.ユーザーは自分の判断に基づいて、どの項目を削除するか、どの項目を保留するかを決定します.
この目的を達成するためには、オペレーティングシステムは、単一のプロジェクトとしてブラウザに報告するのではなく、空間的に細分化されたマシンアプリケーションを使用することと同じソースを記憶する必要がある.
キャッシュアドバイス-応答要求
どれだけ多くの内容をキャッシュするつもりであっても、ServiceWorkerはいつまでに内容をキャッシュするべきかを教えない限り、ServiceWorkerはキャッシュをアクティブに使用しません.以下は要求を処理するためのポリシーです.
キャッシュのみ(cache only)
適用することができます.サイトの「バージョン」は静的なコンテンツの任意のリソースであると考えられます.installイベントでこれらのリソースをキャッシュしてください.要求を処理するときに依存してもいいです.
self.addEventListener('fetch', (event) => {
  //                  ,
  //                  。
  event.respondWith(caches.match(event.request));
});
このような状況を特殊な方法で処理する必要はありませんが、「キャッシュバック」はこのようなポリシーをカバーしています.
ネットワークのみ(network only)
該当するオフラインリソースの対象がありません.例えば、analtics pings、非GET要求です.
self.addEventListener('fetch', (event) => {
  event.respondWith(fetch(event.request));
  //          event.respondWith,    
  //           
});
このような状況を特殊な方法で処理する必要はありませんが、「キャッシュバック」はこのようなポリシーをカバーしています.
キャッシュが優先され、キャッシュがないとネットワークに戻る(Cache,falling back to network)
適用:キャッシュ優先で構築されると、このようなポリシーは、ほとんどの要求を処理する際に使用されるポリシーは、着信要求によって異なり、他のポリシーは例外があります.
self.addEventListener('fetch', (event) => {
  event.respondWith(async function() {
    const response = await caches.match(event.request);
    return response || fetch(event.request);
  }());
});
ここで、キャッシュされたリソースに対して「Cache only」を提供する行為は、キャッシュされていないリソース(すべての非GET要求を含み、それらはキャッシュされていないので)に対して「Network only」という行為を提供する.
キャッシュとネットワークの競争
読み取り速度が遅いハードディスクに格納されている小型資源.
古いハードディスク、ウイルススキャンプログラム、および高速インターネットなどの要素が存在する場合、ハードディスクから取得したものよりもインターネットからリソースを取得するのが速いかもしれません.しかし、ネットワークを通じてユーザ機器に保存されているコンテンツを取得するのは、無駄な流量行為ですので、この点を覚えてください.
// Promise.race          ,        promise 
// fulfilling  reject ,    Promise.race    reject。
//          race  :
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    //   promises     promise  。
    promises = promises.map(p => Promise.resolve(p));
    //        promise     resolve,   promise    resolve 
    promises.forEach(p => p.then(resolve));
    //        promise reject ,   promise    resject 
    promises.reduce((a, b) => a.catch(() => b))
      .catch(() => reject(Error("All failed")));
  });
};

self.addEventListener('fetch', (event) => {
  event.respondWith(
    promiseAny([
      caches.match(event.request),
      fetch(event.request)
    ])
  );
});
ネットワークでの取得に失敗してキャッシュに戻る(Network falling back to cache)
頻繁に更新されるリソースを高速で修復します.例えば、文章、顔写真、ソーシャルメディアの時間軸、ゲームランキングなどです.
これはオンラインユーザに最新のコンテンツを提供することができるという意味ですが、オフラインユーザが取得したのは古いキャッシュバージョンです.ネットワーク要求が成功すれば、キャッシュを更新する必要があります.
しかし、この方法には欠陥があります.ユーザーのネットワークが断続的に途切れたり、あるいは速度が超遅い場合、ユーザーは自分のデバイスからより良い、受け入れ可能なコンテンツを得る前に、長い時間をかけてネットワーク要求の失敗を待つかもしれません.このようなユーザー体験は非常に悪いです.次のより良い解決策を確認してください.「 」.
self.addEventListener('fetch', (event) => {
  event.respondWith(async function() {
    try {
      return await fetch(event.request);
    } catch (err) {
      return caches.match(event.request);
    }
  }());
});
キャッシュしてからネットワークにアクセスします.
頻繁な内容を更新します.例えば、文章、ソーシャルメディアタイムライン、ゲームランキングなどです.
このようなポリシーは、ページが2つの要求を開始する必要があります.1つはキャッシュを要求するネットワークです.まずキャッシュデータを示し、その後、ネットワークデータが到着すると、ページを更新します.
新しいデータを取得する際には、現在のデータだけを置き換えることができますが、大きなコンテンツがあるとデータが中断されます.基本的には、ユーザが読んでいるか操作中のコンテンツが突然消えないようにしてください.
Twitterは古いコンテンツに新しいコンテンツを追加し、スクロールの位置を調整して、ユーザーに感知されないようにしています.これは可能です.Twitterは通常、内容を最も線形的にする順序を維持しています.私はtrined-to-thrillのためにこのモードをコピーして、できるだけ早く画面の内容を取得しますが、それが現れたら最新のコンテンツを表示します.ページのコードです.
async function update() {
  //           
  const networkPromise = fetch('/data.json');

  startSpinner();

  const cachedResponse = await caches.match('/data.json');
  if (cachedResponse) await displayUpdate(cachedResponse);

  try {
    const networkResponse = await networkPromise;
    const cache = await caches.open('mysite-dynamic');
    cache.put('/data.json', networkResponse.clone());
    await displayUpdate(networkResponse);
  } catch (err) {
   
  }

  stopSpinner();

  const networkResponse = await networkPromise;

}

async function displayUpdate(response) {
  const data = await response.json();
  updatePage(data);
}
通常のキャンセル
ネットワークとキャッシュからリソースを提供できない場合は、通常のキャンセルポリシーが必要です.
副次的な画像、例えば顔写真、失敗したPOST要求、「オフライン時は利用できません」ページに適用します.
self.addEventListener('fetch', (event) => {
  event.respondWith(async function() {
    //         
    const cachedResponse = await caches.match(event.request);
    if (cachedResponse) return cachedResponse;

    try {
      //      
      return await fetch(event.request);
    } catch (err) {
      //       ,      :
      return caches.match('/offline.html');
      //   ,        URL Headers,          
      //   :      
    }
  }());
});
返品された項目は「インストール依存項」かもしれません.
ServiceWorker-side templating
サーバ応答のページをキャッシュできませんでした.
サーバー上でページをレンダリングすると、速度を上げることができますが、これはキャッシュに意味のない状態データを含むことを意味します.例えば、「ロギンドin as...」.ページがServiceWorkerによって制御されると、JSONデータとテンプレートを選択してレンダリングすることができます.
importScripts('templating-engine.js');

self.addEventListener('fetch', (event) => {
  const requestURL = new URL(event.request);

  event.responseWith(async function() {
    const [template, data] = await Promise.all([
      caches.match('/article-template.html').then(r => r.text()),
      caches.match(requestURL.path + '.json').then(r => r.json()),
    ]);

    return new Response(renderTemplate(template, data), {
      headers: {'Content-Type': 'text/html'}
    })
  }());
});
締め括りをつける
その中の一つだけを選ぶ必要はありません.要求URLに応じて様々な方法を選択して使用してもいいです.例えば、trined-to-thrillで使用しました.
  • は、インストール時にキャッシュされ、静的UI
  • に適用される.
  • は、ネットワーク応答時にキャッシュし、ネットワークピクチャおよびデータ
  • に適用される.
  • は、バッファから取得し、失敗したらネットワークに戻ると、ほとんどの要求
  • に適用される.
  • はキャッシュから取得し、ネットワーク検索結果
  • に適用するように要求する.
    要求に応じて、何をするかを決めることができます.
    self.addEventListener('fetch', (event) => {
      // Parse the URL:
      const requestURL = new URL(event.request.url);
    
      // Handle requests to a particular host specifically
      if (requestURL.hostname == 'api.example.com') {
        event.respondWith(/* some combination of patterns */);
        return;
      }
      // Routing for local URLs
      if (requestURL.origin == location.origin) {
        // Handle article URLs
        if (/^\/article\//.test(requestURL.pathname)) {
          event.respondWith(/* some other combination of patterns */);
          return;
        }
        if (requestURL.pathname.endsWith('.webp')) {
          event.respondWith(/* some other combination of patterns */);
          return;
        }
        if (request.method == 'POST') {
          event.respondWith(/* some other combination of patterns */);
          return;
        }
        if (/cheese/.test(requestURL.pathname)) {
          event.respondWith(
            new Response("Flagrant cheese error", {
              status: 512
            })
          );
          return;
        }
      }
    
      // A sensible default pattern
      event.respondWith(async function() {
        const cachedResponse = await caches.match(event.request);
        return cachedResponse || fetch(event.request);
      }());
    });
    感謝の意を表す
    下記の各位に感謝します.本文のために可愛いアイコンを提供します.
  • Code,著者:buzyrobot
  • Calendar,著者:Scrott Lewis
  • Network,著者:Ben Rizzo
  • SD,著者:Thomas Le Bas
  • CPU,著者:iconsmand.com
  • Trass,著者:trsnik
  • Notification、著者:@daosme
  • Layout、著者:ミスターPixel
  • Cloud、著者:P.J.Onii
  • 同時にJeff Posnickに感謝します.私が「リリース」ボタンを押す前に、多くのところに明らかなエラーがあります.
    拡大して読む
  • Intro to ServiceWorkers
  • Is ServiceWorker ready?追跡主流ブラウザのServiceWorkerの実現状態
  • JavaScript promises,there and back again-Promiseガイド