[再現]従来のブラウザキャッシュとサービスワーカーの比較

1.従来のHTTPブラウザーのキャッシュ戦略

Webページのライフサイクルでは、ユーザーがページを開く時間を短縮するために、通常、開発者は多くのキャッシュを設定します。これらには以下が含まれます:

  • ブラウザのキャッシュ

  • プロキシサーバーキャッシュ(CDNキャッシュ)

  • サーバーキャッシュ

  • データベースキャッシュ

さまざまなキャッシュ。これらのキャッシュのほとんどはフロントエンドとは関係がなく、フロントエンドの開発者によって制御されていません。ブラウザのキャッシュはフロントエンドに近いですが、基本的にサーバーによって制御されています。

Service Workerが登場する前は、ブラウザーキャッシュは主にHTTPキャッシュ戦略とブラウザーの組み込みストレージ機能(Cookie、ローカルストレージ、セッションストレージなど)によって提供されていました。その中でも、HTTPキャッシングは必須であり、ルートは非常に人気があり、ブラウザーサポートも非常に優れているため、最も一般的に使用されるブラウザーキャッシングテクノロジーです。しかし、ブラウザの組み込みストレージ機能を介してキャッシュを実現することは、対照的に、それほどハイエンドな雰囲気ではありません。この方法には標準のパラダイムがないため、JSで制御できますが、HTTPキャッシングよりも柔軟性がありますが、効果はプログラマーのレベルにのみ依存し、使用する統一されたホイールがないため、この方法は小さいですほとんど問題は気候ではありません。

HTTPキャッシングのいくつかの使用法は次のとおりです。


  • HTTPプロトコルが設計された当初から、Expiresヘッダーはプロトコルの起草者によって考えられていましたが、当然、対応する機能、つまりExpiresこのヘッダーも持っています。ブラウザがリクエストするたびに、サーバーはこれを対応するメッセージに追加できますExpires。その典型的な値は次のようになります:

Expires: Tue, 01 May 2018 11:37:06 GMT

つまり、世界のリソースの調整が2018/05/01 11:37:06ちょうど期限切れになったとき、私はtimeを要求し2018/05/01 07:37:06ますCtrl+F5。これによりリフレッシュを使用しない限り、リクエストリソースがキャッシュを直接使用してから4時間以内に、リソースは4時間後に期限切れになりますただし、このコントロールは明らかに正確ではありません。これはHTTP1.0プロトコルで指定されたヘッダーです。現在はすべてHTTP2.0を使用しており、HTTP1.1は完全に普及しているため、当然、この機能はあまり使用されていません。

  • Cache-Controlヘッダー
    Expiresは有効期限のみを制御できます。リクエストされたリソースが有効期限より前に更新された場合、表示または機能の問題が発生する可能性があります。HTTPプロトコルがバージョン1.1に更新されたときに、このような理由から、新しいヘッダが追加されましたCache-Controlし、規定:両方の場合Cache-Control前者が有効ですこれには、一般的に使用される次の値から選択できます典型的な値は次のようになります。Expirespublic private max-age s-maxage no-cache no-store

Cache-Control: s-maxage=300, public, max-age=60

各フィールドの意味をよりよく説明するために、ブラウザがリソースをリクエストする手順について説明しましょう。

  1. リクエストがキャッシュにヒットするかどうかを判断し、ヒットする場合は手順2に進み、ヒットしない場合は手順3に進みます。

  2. キャッシュが期限切れかどうかを判断し、期限が切れていない場合は直接戻ります。期限切れの場合は、手順3に進み、キャッシュ情報を取得します。

  3. ブラウザはサーバーにリソースを要求します。

  4. サーバーはキャッシュ情報を判断し、リソースが更新されていない場合は戻り、キャッシュ情報304がない場合やリソースが更新されている場合は200戻り、リソースを返します。

  5. ブラウザは、応答ヘッダーに従ってキャッシュを保存するかどうかを決定します(no-storeキャッシュ情報が保存されていない場合のみ)。

s-maxage共有キャッシュ時間を示します。単位はs、つまり5分です。
publicこれは、他のセッションで使用できる共有キャッシュであることを
max-age意味します。意味はs-maxage同じですがprivate、状況で使用されます。
no-cacheこの戦略では、ブラウザーはステップ2をスキップします、そしてキャッシュ情報を持ってサーバーへのリクエストを開始します。
no-storeこの戦略では、ブラウザーはステップ5をスキップします。キャッシュ情報がないため、ブラウザーが要求するたびに、最初の要求(Ctrl + F5効果)のようにキャッシュ情報が表示されません。

  • 上記のLast-Modified / If-Modified-Since
    では、ブラウザが情報をキャッシュしている場合、キャッシュされた情報を使用してリクエストを開始します。この情報はどのようにして取得されるのですか?リクエストのヘッダーに入れる方法は?
    サーバーがリクエストに応答すると、Cache-Controlヘッダーに加えてLast-Modified、リソースのサーバー更新時間を指定するヘッダーも返すことがわかりました。ブラウザ側でリソースが期限切れになると(max-ageまたはによって決定no-cache)、ブラウザはキャッシュされた情報を取得してリクエストを開始します。この情報はIf-Modified-Since、通常Last-Modified最後のResponseのであるRequestによって指定さます。一般的な値は次のとおりです。

//Response:
Last-Modified: Sat, 01 Jan 2000 00:00:00 GMT
//Request:
If-Modified-Since: Sat, 01 Jan 2000 00:00:00 GMT
  • Etag / If-None-Match
    Last-Modified/If-Modified-Sinceはより詳細な制御を提供しますが、リソース時間の制御しか提供できず、第2レベルまでしか正確でないため、開発者は依然として不満を抱くことがあります一部のリソースが急速に変化する場合、または一部のリソースが定期的に生成されるが、コンテンツが同じ場合、これらの状況Last-Modified/If-Modified-Sinceはあまり適用されません。
    このため、HTTP 1.1はEtag/If-None-Matchこれら2つのヘッダーとその使用法を指定Last-Modified/If-Modified-Sinceしており、1つは応答用で、もう1つは要求用です。それEtagは時間ではなく、サーバーによって指定されたラベル(通常、リソースのコンテンツ、サイズ、および時間のハッシュ値)です。このようにして、サーバーはこのヘッダーを通じてリソースキャッシング戦略をより正確に制御できます。
    ヘッド制御がより洗練されているため、同様に、その優先順位は高くなりますLast-Modified/If-Modified-Sinceと同じように、Cache-Control高いですExpires

2.サービスワーカーの原則

HTTPキャッシュはすでに十分強力であるため、開発者の不満は何ですか?バックエンドの開発者は当然不満はなく、フロントエンドの開発者はつぶやきそうです。「なぜブラウザーの問題をバックエンドに依存する必要があるのですか?バックエンドはデータを提供するだけです。キャッシュを制御したいのです。」 。一部の人々は実際にこれを試しました。つまり、ローカルストレージまたはセッションストレージを使用してデータを格納しますが、この方法には、非同期ストレージ、静的リソースストレージ、URLマッチング、リクエストインターセプト、その他の機能など、多くの主要なブラウザーインフラストラクチャがありません。Service Workerの出現は、これらのインフラストラクチャの問題の欠如を埋めます。

Service Workerはキャッシュ用に特別に設計されていないこと、およびWebアプリケーションのプッシュや長いバックグラウンド計算などの問題を解決できることを指摘する必要があります。強力な機能により、きめ細かなキャッシュ制御を解決できます。これは、本質的には新しいJavaScriptスレッドであり、メインのJavaScriptスレッドとは異なるコンテキストで実行されるためです。サービスワーカースレッドは完全に非同期であるように設計され、そのように元々メインスレッド内のいくつかの同期のAPI、XMLHTTPRequestおよびは、localStorageサービス員に使用することができません。

メインのJavaScriptスレッドはDOMを担当するスレッドですが、サービスワーカースレッドはDOMにアクセスできないように設計されています。通常、クライアント開発に携わった開発者は、UIスレッドは1つしか存在できないことを知っています。そうしないと、UI全体の制御で予期しない問題が発生します。UIがスムーズでスタックしないようにするための原則は、UIスレッドで多くの計算と同期IO処理を行わないようにすることです

  1. swスレッドは、サーバーとのデータ通信に使用できます(Service Workerのコンテキストには、フェッチおよびプッシュAPIが組み込まれています)。

  2. UIの応答に影響を与えずに多数の複雑な計算を実行するために使用できます。

  3. すべてのリクエストをインターセプトでき(フェッチイベントを監視することで、ネットワークリソースに対するすべてのリクエストがイベントをトリガーします)、組み込みの完全非同期ストレージシステム(完全に非同期であり、あらゆる種類のネットワークリソースを保存できるCaches属性)を備えています。これは、キャッシュを細かく制御するための鍵です。

Service Worker機能は非常に強力であることがわかります。特に、すべての要求をインターセプトしてプロキシサーバーとして機能する機能は強力で危険です。したがって、この機能が不本意な人によって使用されないようにするために、Service WorkerはHTTPSのOriginで実行する必要があり、localhostも安全であると見なされ、デバッグと開発に使用できます。

3. Service Workerのキャッシュ

前述のように、Service Workerをキャッシュに使用する場合、重要なのはFetchイベント監視してCacheリソース管理することですが、それらを使用する前にService Workerをアクティブにする必要があります。Service Workerのアクティブ化は、次の手順を実行します。

  1. ブラウザは、Service Workerが現在のページに登録されている(合格navigator.service.Worker.register('/sw.js'))ことを検出しました。

  2. ブラウザがダウンロードsw.jsして実行され、インストールフェーズが完了します。

  3. サービスワーカーは、Originの他のワーカーの障害を待機してから、アクティブ化フェーズを完了します。

  4. Service Workerが有効になります。有効範囲は現在のページではなく、Origin全体であることに注意してくださいしかしregister()、成功後に開かれたページのみがSWによって制御されます。したがって、SWが完全に制御できるようにするには、通常、登録スクリプトを実行するページをリロードする必要があります。

次の図は、サービスワーカー全体のライフプロセスを示しています。

Service Workerがキャッシュを制御する方法を紹介する簡単な例を以下に示します。通常、キャッシュindex.htmlは次の場所に登録されています。
コードリスト:index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <link href="style/style-1.css" rel-"stylesheet">
    </head>
    <body>
        <img src="image/image-1.png" />
        <script async src="js/script-1.js"></script>
        <script>
            if ('serivceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
                    .then(reg => console.log('Service worker registered successfully!'))
                    .catch(err => console.log('Service worker failed to register!'));
            }
        </script>
    </body>
</html>

このページにも4つのリソースがあることがわかりstyle-1.css image-1.png script-1.jsますsw.jsページ内のJSがregisterメソッドに対して実行されると、ブラウザーsw.jssw.jsコンテンツに従ってService Workerをダウンロードてインストールする準備をします。
コードリスト:sw.js

let cacheName = 'indexCache';
self.addEventListener('install', event => {
    //waitUntil接受一个Promise,直到这个promise被resolve,安装阶段才算结束
    event.waitUntil(
        caches.open(cacheName)
            .then(cache => cacheAll(['/style/style-1.css',
                                     '/image/image-1.png',
                                     '/script/script-1.js',
                                    ]))
                    );
});

//监听activate事件,可以在这个事件里情况上个sw缓存的内容
self.addEventListener('activate', event => ...}

//监听fetch事件,可以拦截所有请求并处理
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request)
            .then(res => {
                //1. 如果请求的资源已被缓存,则直接返回
                if (res) return res;
                //2. 没有,则发起请求并缓存结果
                let requestClone = event.request.clone();
                return fetch(requestClone).then(netRes => {
                    if(!netRes || netRes.status !== 200) return netRes;
                    let responseClone = netRes.clone();
                    caches.open(cacheName).then(cache => cache.put(requestClone, responseClone));
                    return netRes;
                });
            })
    );
});

Service Workerはインストール時に3つのリソースファイルをキャッシュすることがわかります。Originの下のページがこれらの3つのリソースのリクエストを次に開始する場合、Fetchイベントによってインターセプトされ、キャッシュを直接使用して戻ります。他のリソースに対してリクエストが行われた場合、ネットワークリソースが応答として使用され、これらのリソースは再度保存されます。

非常に強力なキャッシュ制御機能が数十行のコードで完了することがわかります。また、リソースの制御方法に応じて、いくつかの特定のリソースを独自に処理することもできます。それでも解決すべき問題があります。つまり、リソースが更新された場合、キャッシュは何をすべきですか?現在、これを行うには2つの方法があります。

  1. sw.jsファイルを更新します。ブラウザーがインストールsw.jsが異なることを(ハッシュ値を計算することにより)検出すると、ブラウザーはService Workerを再インストールします。ブラウザーがサーバーを使用するように、インストールおよびアクティブ化プロセス中に以前のキャッシュをクリアできます最新のリソース。

  2. 上記の例のようstyle-2.cssstyle-1.css代わり使用できるリソースファイルのバージョン管理なので、Service Workerは新しいリソースを使用してキャッシュします。もちろん、バージョン番号はそれほど単純なものであってはならずファイル内容のハッシュ値+変更時間+サイズをバージョン番号として使用するが最善です。

上記の2つの方法は信頼できます。1つ目の方法の信頼性はブラウザーによって保証され、2つ目の方法は試行されてテストされています。現在、ほとんどのWebサイトは2つ目の方法と同様に静的リソース更新戦略を使用しています。メソッドのバージョン管理リソースのバージョン番号を調整するときはsw.js、リソースリストを更新する必要があるため、これら2つの方法は通常一緒に混在していsw.jsます。これにより、ファイル自体が変更されます。

注意すべきもう一つの問題は、あるあるsw.jsそれはまた、HTTPのキャッシング戦略によってキャッシュされますがsw.jsファイル名に対してバージョン管理実行することで、キャッシュされたService Workerインストールファイルが原因でリソースがタイムリーに更新されない問題を回避できます。

4. Service Workerキャッシング拡張アプリケーション

前述のように、Service Workerの登場はブラウザキャッシュの洗練された制御の問題を解決するためだけのものではありませんプロキシサーバーとして機能し(すべてのリクエストをインターセプトすることで実装)、HTTPキャッシュでは実現できない機能であるオフラインアプリケーションを実現できますHTTPキャッシング戦略では、リソースがサーバーによって指定された有効期限を過ぎた場合、リクエストを開始する必要があるため、ネットワーク接続に問題が発生すると、Webサイト全体に機能上の問題が発生します。Service Workerの制御下にあるキャッシュは、コードでネットワーク接続の問題を見つけて、キャッシュされたリソースを直接返すことができます。この方法で返された応答はブラウザに対して透過的であり、応答はサーバーから送り返されたリソースであると見なされます。

上記の機能とService Workerが提供するプッシュ機能のおかげで、Webベースのアプリケーションはすでにネイティブアプリケーションに匹敵します。Googleはこの種のWebアプリケーションをPWA(Progressive Web Application)と呼んでいます。

Webアプリケーションの機能がますます強力になるにつれ、AndroidとIOSのシェルアプリケーションがますます増え、最近、Microsoftはwin 10のUWPアプリケーションをPWAモードで開発できることも発表しました。これまでのところ、クロスプラットフォームアプリケーション開発の主流のテクノロジーはますます明確になっています。業界がJava-SWT、QT、Xamarinの試みを経験した後、ブラウザーで開始された一連のテクノロジーであるHTML + CSS + Javascriptがクロスプラットフォームアプリケーションになりました。主流の技術を開発。

おすすめ

転載: blog.csdn.net/github_38885296/article/details/104013222