0

我在我的网站上介绍服务工作者。并且我使用app-shell方法来响应requests.Below是我的代码结构。服务工作者app-shell PHP实现

serviceWorker.js

self.addEventListener("fetch", function(event) { 
    if (requestUri.indexOf('-spid-') !== -1) { 
    reponsePdPage(event,requestUri); 
    }else{ 
    event.respondWith(fetch(requestUri,{mode: 'no-cors'}).catch(function (error){ 
     console.log("error in fetching => "+error); 
     return new Response("not found"); 
     }) 
    ); 
    } 
}); 


function reponsePdPage(event,requestUri){ 
    var appShellResponse=appShellPro(); 
    event.respondWith(appShellResponse); //responds with app-shell 
    event.waitUntil(
     apiResponse(requestUri)  //responds with dynamic content 
    ); 
} 

function appShellPro(){ 
    return fetch(app-shell.html); 
} 
function apiResponse(requestUri){ 
    var message=['price':'12.45cr']; 
    self.clients.matchAll().then(function(clients){ 
    clients.forEach(function (client) { 
     if(client.url == requestUri) 
      client.postMessage(JSON.stringify(message)); 
    }); 
    }); 
} 

的App-shell.html

<html> 
    <head> 
    <script> 
      if ('serviceWorker' in navigator) { 
       navigator.serviceWorker.onmessage = function (evt) { 
       var message = JSON.parse(evt.data); 
       document.getElementById('price').innerHTML=message['price']; 
       } 
      } 
    </script> 
    </head> 
    <body> 
     <div id="price"></div> 
    </body> 
</html> 

serviceWorker.js是我唯一的服务人员文件。每当我收到-spid- url的请求时,我会调用reponsePdPage函数。在reponsePdPage函数中,我首先回应了app-shell.html。之后我打电话apiResponse功能,调用PostMessage的发送动态data.The 听者的留言信息写在APP-shell.html

我面临的问题是,有时会在收听者注册之前调用发送消息。这意味着apiResponse调用发送消息,但它们不是注册该事件的监听器。所以我无法捕获数据。?在我的实现中他们的错误。

回答

2

我将专注于最后一点,关于服务工作者和受控页面之间的沟通。该问题与您提供的许多其他细节分开,例如使用PHP并采用App Shell模型。

正如您所观察到的那样,由于服务工作者中的代码以及HTML的解析和执行在不同的进程中执行,所以存在竞争条件。当服务人员拨打client.postMessage()时,我并不惊讶onmessage处理程序尚未在页面中建立。

如果您希望将服务工作人员的信息传递给受控页面,同时避免竞争条件,则有几个选项。

第一个也许是最简单的选择是改变通信方向,并让受控页面使用postMessage()向服务工作者发送请求,服务工作者然后用相同的信息作出响应。如果采取这种方法,您将确保受控页面已准备好进入服务工作人员的响应。有一个完整的例子here,但这里的相关位的简化版本,它采用了基于承诺的包装器来处理从服务人员接收到异步响应:

控制页面内:

function sendMessage(message) { 
    // Return a promise that will eventually resolve with the response. 
    return new Promise(function(resolve) { 
    var messageChannel = new MessageChannel(); 
    messageChannel.port1.onmessage = function(event) { 
     resolve(event.data); 
    }; 

    navigator.serviceWorker.controller.postMessage(message, 
     [messageChannel.port2]); 
    }); 
} 

里面的服务人员:

self.addEventListener('message', function(event) { 
    // Check event.data to see what the message was. 
    // Put your response in responseMessage, then send it back: 
    event.ports[0].postMessage(responseMessage); 
}); 

其它方法包括设置在IndexedDB的值在服务工作者内部,然后在加载后从受控页面读取。最后,您可以从缓存存储API中检索HTML,将其转换为字符串,修改该字符串以包含内联的相关信息,然后使用包含修改过的HTML的新的Response进行响应。不过这可能是最重量级和最脆弱的方法。

+0

如果我使用第一种方法,即从受控页面调用postMessage,这意味着服务工作线程将保持闲置直到我从客户端调用postMessage。在第二种方法中,如果服务未完成,我检查IndexedDB中的值。 –

+0

我不理解服务员“闲置”的担忧。这不是由您来管理服务工作线程的执行。浏览器将决定杀死软件或者基于你的控制之外的启发式软件保持它活着。重要的是你可以调用'postMessage()',你将(最终)得到一个响应。 –

+0

我的意思是说,因为所有的后端计算都是在第二次调用中完成的(意味着** apiResponse **方法),并且服务工作人员只有在客户端调用post消息时才会启动此方法。在返回app-shell之后启动api调用并将结果放入缓存不是一个好主意。 –