2017-01-28 3 views
1

ServiceWorkerのClients.claim APIの周りに私の頭を包み込むのに問題があります。私が理解しているところでは(herehere)、サービスワーカーでclaim()と呼ぶことができます。イベントをアクティブにして、ページを更新してServiceWorkerを初期化しないようにします。私はそれを動作させることはできませんし、常にリフレッシュする必要が終了します。サービスワーカーインサイドページをリロードしないようにServiceWorkerを初期化するときに、どのようにクライアントを要求できますか?

:ここに私のコードです

self.addEventListener('install', function (event) { 

    self.skipWaiting(); 

    event.waitUntil(caches.open(CURRENT_CACHE_DICT.prefetch) 
    .then(function(cache) { 
     var cachePromises = PREFETCH_URL_LIST.map(function(prefetch_url) { 
     var url = new URL(prefetch_url, location.href), 
      request = new Request(url, {mode: 'no-cors'}); 

     return fetch(request).then(function(response) { 
      if (response.status >= 400) { 
      throw new Error('request for ' + prefetch_url + 
       ' failed with status ' + response.statusText); 
      } 
      return cache.put(prefetch_url, response); 
     }).catch(function(error) { 
      console.error('Not caching ' + prefetch_url + ' due to ' + error); 
     }); 
     }); 

     return Promise.all(cachePromises).then(function() { 
     console.log('Pre-fetching complete.'); 
     }); 
    }).catch(function(error) { 
     console.error('Pre-fetching failed:', error); 
    }) 
); 
}); 

self.addEventListener('activate', function (event) { 

    // claim the scope immediately 
    // XXX does not work? 
    //self.clients.claim(); 

    event.waitUntil(self.clients.claim() 
    .then(caches.keys) 
    .then(function(cache_name_list) { 
     return Promise.all(
     cache_name_list.map(function() {...} 
    ); 
    }) 
); 
}); 

以上実行されますが、私はリフレッシュする必要が終わるとChrome ServiceWorkerの内部でIllegal invocationエラーを検出しましたよ。 waitUntilハンドラからclients.claimを削除し、以前のコメントを外してもエラーは発生しませんが、まだ更新する必要があります。デバッガは示しています

Console: {"lineNumber":128,"message":"Pre-fetching complete.","message_level":1,"sourceIdentifier":3,"sourceURL":""} 
Console: {"lineNumber":0,"message":"Uncaught (in promise) TypeError: Illegal invocation","message_level":3,"sourceIdentifier":1,"sourceURL":""} 

リフレッシュは、次のようにトリガーされます。

function waitForInstallation(registration) { 
    return new RSVP.Promise(function(resolve, reject) { 
     if (registration.installing) { 
     registration.installing.addEventListener('statechange', function(e) { 
     if (e.target.state == 'installed') { 
      resolve(); 
     } else if (e.target.state == 'redundant') { 
      reject(e); 
     } 
     }); 
    } else { 
     resolve(); 
    } 
    }); 
} 

// refreshing should not be necessary if scope is claimed on activate 
function claimScope(installation) { 
    return new RSVP.Promise(function (resolve, reject) { 
    if (navigator.serviceWorker.controller) { 
     resolve(); 
    } else { 
     reject(new Error("Please refresh to initialize serviceworker.")); 
    } 
    }); 
} 

rJS(window) 
    .declareMethod('render', function (my_option_dict) { 
    var gadget = this; 

    if ('serviceWorker' in navigator) { 
     return new RSVP.Queue() 
     .push(function() { 
      return navigator.serviceWorker.register(
      my_option_dict.serviceworker_url, 
      {scope: my_option_dict.scope} 
     ); 
     }) 
     .push(function (registration) { 
      return waitForInstallation(registration); 
     }) 
     .push(function (installation) { 
      return claimScope(installation); 
     }) 
     .push(null, function (my_error) { 
      console.log(my_error); 
      throw my_error; 
     }); 
    } else { 
     throw new Error("Browser does not support serviceworker."); 
    } 
    }); 

質問
どのように正しくclaimを使用してServiceWorkerをアクティブにするためにリフレッシュされることからページを防ぐのですか?私が指摘したリンクのうち、controllerを明示的にチェックする必要はありませんでしたが、ServiceWorkerがアクティブな場合、コントローラにアクセス可能であると想定しています。

いくつかの情報を流してくれてありがとう。

編集:
以下のヘルプから理解してください。

var SW = navigator.serviceWorker;  

function installServiceWorker(my_option_dict) { 
    return new RSVP.Queue() 
    .push(function() { 
     return SW.getRegistration(); 
    }) 
    .push(function (is_registered_worker) { 

     // XXX What if this isn't mine? 
     if (!is_registered_worker) { 
     return SW.register(
      my_option_dict.serviceworker_url, { 
      "scope": my_option_dict.scope 
      } 
     ); 
     } 
     return is_registered_worker; 
    }); 
} 

function waitForInstallation(registration) { 
    return new RSVP.Promise(function(resolve, reject) { 
    if (registration.installing) { 
     // If the current registration represents the "installing" service 
     // worker, then wait until the installation step completes (during 
     // which any defined resources are pre-fetched) to continue. 
     registration.installing.addEventListener('statechange', function(e) { 
     if (e.target.state == 'installed') { 
      resolve(registration); 
     } else if (e.target.state == 'redundant') { 
      reject(e); 
     } 
     }); 
    } else { 
     // Otherwise, if this isn't the "installing" service worker, then 
     // installation must have beencompleted during a previous visit to this 
     // page, and the any resources will already have benn pre-fetched So 
     // we can proceed right away. 
     resolve(registration); 
    } 
    }); 
} 

// refreshing should not be necessary if scope is claimed on activate 
function claimScope(registration) { 
    return new RSVP.Promise(function (resolve, reject) { 
    if (registration.active.state === 'activated') { 
     resolve(); 
    } else { 
     reject(new Error("Please refresh to initialize serviceworker.")); 
    } 
    }); 
} 

rJS(window) 

    .ready(function (my_gadget) { 
    my_gadget.property_dict = {}; 
    }) 

    .declareMethod('render', function (my_option_dict) { 
    var gadget = this; 

    if (!SW) { 
     throw new Error("Browser does not support serviceworker."); 
    } 

    return new RSVP.Queue() 
     .push(function() { 
     return installServiceWorker(my_option_dict), 
     }) 
     .push(function (my_promise) { 
     return waitForInstallation(my_promise); 
     }) 
     .push(function (my_installation) { 
     return claimScope(my_installation); 
     }) 
     .push(function() { 
     return gadget; 
     }) 
     .push(null, function (my_error) { 
     console.log(my_error); 
     throw my_error; 
     }); 
    }); 

答えて

5

は、まず、あなたがしているので、あなたのコードのタイプミスのエラーになっているようだ:

// runs while an existing worker runs or nothing controls the page (update here) 
self.addEventListener('install', function (event) { 

    event.waitUntil(caches.open(CURRENT_CACHE_DICT.dictionary) 
    .then(function(cache) { 
     var cache_promise_list = DICTIONARY_URL_LIST.map(function(prefetch_url) {...}); 

     return Promise.all(cache_promise_list).then(function() { 
     console.log('Pre-fetching complete.'); 
     }); 
    }) 
    .then(function() { 

     // force waiting worker to become active worker (claim) 
     self.skipWaiting(); 

    }).catch(function(error) { 
     console.error('Pre-fetching failed:', error); 
    }) 
); 
}); 

// runs active page, changes here (like deleting old cache) breaks page 
self.addEventListener('activate', function (event) { 

    event.waitUntil(caches.keys() 
    .then(function(cache_name_list) { 
     return Promise.all(
     cache_name_list.map(function(cache_name) { ... }) 
    ); 
    }) 
    .then(function() { 
     return self.clients.claim(); 
    }) 
); 
}); 

トリガスクリプトを:これは私のために働かせました。下部のノートを参照してください。

また、skipWaiting()Clients.claim()は、単一の要求に新しい SWをインストールして起動するん両方。しかし、当然のことながら、リロードした後は、CSSのような静的資産しか得られません。

skipWaiting()Clients.claim()が装備されている場合でも、更新されたstaticの内容を新しいhtmlやスタイルのように表示するには2ページのリロードが必要です。

ページのロード#1

  • 要求sw.jsに作られ、そしてsince SW contents is changedinstallイベントが、その上に発射されます。
  • あなたinstallハンドラでself.skipWaiting()を持っているので、またactivateイベントは、解雇されます。
  • したがって、activateハンドラが実行され、self.clients.claim()コールがあります。これはSWの前身であるすべてのクライアントのコントロールを引き継ぐようにSWに命令します。
  • この時点で、キャッシュ内の資産が更新され、ページはすべて新しいサービスワーカーによって制御されます。サービスワーカの範囲内のAjaxリクエストは、たとえば、キャッシュされた新しいレスポンスを返します。

ページのロード#2

アプリの負荷、およびあなたのSWは、いつものようにリクエストをハイジャックして、キャッシュから応答します。しかし今やキャッシュは最新のものになり、ユーザーはのアセットを新しいアセットで完全ににすることになります。

あなたがなければなりません

Uncaught (in promise) TypeError: Illegal invocationエラーを取得しているエラーによりごactivateハンドラ内で行方不明括弧へ。

event.waitUntil(self.clients.claim() 
    .then(caches.keys) 
    .then(function(cache_name_list) { 
     return Promise.all(
     cache_name_list.map(function() {...} 
    ); <-- Here is our very lonely single parenthesis. 
    }) 
); 

修正するとそのエラーは消えます。

+0

孤独な括弧を付けてくれてありがとう:)ページロード#1は制御を提供し、ページロード#2は#2ロードで新しい静的コンテンツをロードします。したがって、私がpostMessage経由で通信する必要があり、要求をキャッシュしない場合は、負荷#1で十分でしょうか?私が主張しているのになぜ 'navigator.serviceWorker.controller'がまだロード#1上で未定義であるのか知っていますか?ありがとう – frequent

+1

ハハ、あなたは歓迎です=)サービスワーカーのアクティブ化を聞くことができるので、サービスワーカーがアクティブになっている/利用可能なときにコードを実行できます。私はその事件をカバーするために私の最近の答えの一つを更新しました。 [あなたは答えに行くことができます](http://stackoverflow.com/a/41916335/4306828)、最後のセクションで 'if(sw.state == 'activated')'ステートメントをチェックしてください。 –

+0

はいいですね。助けてくれてありがとう!括弧は – frequent

関連する問題