从service worker访问localStorage

44

我希望定期从我的服务工作线程调用API,以发送存储在localStorage中的数据。当用户浏览我的网站时,这些数据将被生成并保存在localStorage中。就像在localStorage中保存统计信息,并通过服务工作线程定期发送一样。我该怎么做?我知道我不能从服务工作线程访问localStorage,必须使用postMessage API。非常感谢您的帮助。


尽管这个问题提到了Angular,但它包含一个可能的答案:https://stackoverflow.com/questions/52721826/how-do-i-access-the-local-storage-using-service-workers-in-angular - Alex028502
@Faiz,你找到任何解决方案了吗? - Dastan Kumar
5个回答

37

由于安全原因,无法从WebWorker进程访问localStorage(以及sessionStorage),它们的结果将为undefined

您需要使用postMessage()将数据发送回工作线程的源代码,并让该代码将数据存储在localStorage中。

应使用localStorage.setItem()localStorage.getItem()保存和获取本地存储中的数据。

更多信息:

Worker.postMessage()

Window.localStorage

下面是伪代码,希望能帮助您入门:

 // include your worker
 var myWorker = new Worker('YourWorker.js'),
   data,
   changeData = function() {
     // save data to local storage
     localStorage.setItem('data', (new Date).getTime().toString());
     // get data from local storage
     data = localStorage.getItem('data');
     sendToWorker();
   },
   sendToWorker = function() {
     // send data to your worker
     myWorker.postMessage({
       data: data
     });
   };
 setInterval(changeData, 1000)

var myWorker = new YourWorker('YourWorker.js'), > new WebWorker 对吗? - roberrrt-s
谢谢GibboK,但是我有几个疑问。你提到的代码将在我的JavaScript之一中运行。为了让它运行,用户必须使网站运行?不是吗?我正在寻找一种即使用户不在我的网站上,服务工作者也可以定期从本地存储发送数据的方法。我的情况可能吗? - Faiz
2
@Faiz 如果你认为我的回答对你有帮助,请不要忘记点击左侧的V图标接受它。谢谢,祝编码愉快 :) - GibboK
1
@FelipeMorales,据我所知,使用服务工作线程应该也可以实现与网页的通信。但请尝试一下并告诉我们结果 :) - GibboK
1
@GibboK,你有什么证据表明在service workers中无法使用localStorage与安全有关?Service workers的MDN页面只提到同步性,但甚至没有支持这一点。 - AntonOfTheWoods
显示剩余3条评论

22

Broadcast Channel API 更容易

几种方法可以在客户端和控制服务工作者之间通信,但是localStorage不是其中之一。IndexedDB是一种方法,但这可能会使PWA变得过于臃肿。

在所有方法中,Broadcast Channel API 是最容易的。它比上述使用MessageChannel APIpostMessage()实现要容易得多。

以下是广播的工作原理

在服务工作者和客户端中都定义一个新的广播频道。

const channel4Broadcast = new BroadcastChannel('channel4');

在 worker 或 client 中发送广播消息的方法:

channel4Broadcast.postMessage({key: value});

要在 worker 或 client 中接收广播消息:

channel4Broadcast.onmessage = (event) => {
    value = event.data.key;
}

如果网站以前没有打开过,而服务工作者刚刚打开了这个新标签页,它不应该等待channel4Broadcast.onmessage被初始化吗? - undefined

16

我一直在使用一个叫做localforage的包,它提供了类似于localStorage的接口,封装了IndexedDB。https://github.com/localForage/localForage

然后你可以通过将它放置在公共目录中,并由Web服务器提供服务,然后在你的Service Worker中调用:self.importScripts('localforage.js');来导入它。


3
IndexedDB 可以在 Service Workers 中运行,这就是为什么 LocalForage 可以工作(它默认使用 IndexedDB)。然而,本问题涉及的是 LocalStorage,在 Service Workers 中无法访问。 - Robert
11
@Robert 的问题是:“我知道我无法访问localStorage……任何帮助将不胜感激。” - Allen
2
尽管这个答案没有包括使用localStorage的解决方案,但它提供了一个非常好的替代方案,易于实现,并且保持了用户熟悉的类似语法。我已经尝试过这个解决方案,它非常容易实现和从本地存储迁移。 - Jim_M

5

0
在我的一个小型Web应用项目中遇到这个问题时,我考虑了以下解决方案: 当用户在线时,可以立即发送数据。当他离线时,我使用SyncEvent.tag属性将信息从客户端发送到serviceworker。就像这样:

//offline_page.html (loads only, when user is offline)

button.onclick = function() {
//on click: store new value in localStorage and prepare new value for synchronization
    localStorage.setItem("actual", numberField.value);
    navigator.serviceWorker.ready.then(function(swRegistration) {
        return swRegistration.sync.register('newval:'+numberField.value);
    });
}

//sw.js

self.addEventListener('sync', function(event) {
//let's say everything in front of ':' is the option, everything afterwards is the value
    let option = event.tag.replace(/(.*?)\:.*?$/, "$1");
    let value = event.tag.replace(/.*?\:(.*?)$/, "$1");

    if(option == "newval") {
        event.waitUntil(
            fetch("update.php?newval="+value)
            .then(function(response) {
                console.log(response);
            })
        );
    }
});

update.php 会在用户上线时将新值保存到后端,但这不会给服务工作者访问本地存储的权限,但它会将每个变更发送给服务工作者。

刚开始逐渐了解这个同步主题。所以我非常感兴趣,这是否有帮助,以及其他人对此解决方案的看法。


在我看来,同步不总是可用(浏览器支持和用户可以禁用它),除此之外,这看起来过于复杂了 - 为什么不简单地发送一条消息呢? navigator.serviceWorker.controller.postMessage({ value:'...' }, [new MessageChannel().port2]); - Leon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接