从窗口上下文

时间:2018-06-14 23:07:35

标签: javascript service-worker

我在sw.js中有一个服务工作者,它使用模板引擎将提交numbre作为版本号。我将缓存名称设置为:

var version = {{ commit_hash }};
self.cacheName = `cache-` + version;

我在worker的安装中将一些脚本添加到缓存中,但是有一些脚本在页面上动态加载。我想在第一次加载时加载所有脚本/ css而不强迫用户等待应用程序首先安装。

我可以使用index.html底部的以下代码获取页面上的所有内容:

var toCache = ['/'];

var css = document.getElementsByTagName("link");
for(el of css) {
    var href = el.getAttribute("href");
    if(href) {
        toCache.push(href);
    }
}
var js = document.getElementsByTagName("script");
for(el of js) {
    var src = el.getAttribute("src");
    if(src) {
        toCache.push(src);
    }
}

工作正常,现在我只需要打开正确的缓存,获取尚未存在的文件,然后存储它们。类似的东西:

toCache.forEach(function(url) {
    caches.match(url).then(function(result) {
        if(!result) {
            fetch(url).then(function(response) {
                caches.open(cacheName).then(cache => {
                    cache.put(url, response)
                });
            });
        }
    });
});

有没有办法从不同文件中的脚本标记内的服务工作者获取cacheName?

是的,我知道我可以通过检查for / of循环来大大简化这一点。我把它拆开了,所以更容易描述。

2 个答案:

答案 0 :(得分:0)

没有

在窗口上下文中执行的JavaScript无法访问SW的上下文,反之亦然。您必须实现某种解决方法。

请记住,您可以使用postMessage在两者之间进行通信。

答案 1 :(得分:0)

使用this博客我能够从服务工作者传回消息并返回。首先,我在sw.js的顶部添加了以下函数:

function clientPostMessage(client, message){
    return new Promise(function(resolve, reject){
        var channel = new MessageChannel();

        channel.port1.onmessage = function(event){
            if(event.data.error){
                reject(event.data.error);
            }
            else {
                resolve(event.data);
            }
        };

        client.postMessage(message, [channel.port2]);
    });
}

这允许我的服务工作者将消息发布到窗口,然后使用promise进行回调。

然后,在我的index.html文件中,我将以下内容添加到脚本标记中:

navigator.serviceWorker.addEventListener('message', event => {
    switch(event.data) {
        case "addAll":
            var toCache = [];
            var css = document.getElementsByTagName("link");
            for(el of css) {
                var href = el.getAttribute("href");
                if(href) {
                    toCache.push(href);
                }
            }
            var js = document.getElementsByTagName("script");
            for(el of js) {
                var src = el.getAttribute("src");
                if(src) {
                    toCache.push(src);
                }
            }
            event.ports[0].postMessage(toCache);
            break;
        default:
            console.log(event.data);
    }
});

这会侦听任何请求消息的服务工作者,如果它是“addAll”消息,它将获取页面上的所有脚本和链接内容并返回脚本数组。

最后,我将以下内容添加到activate中的sw.js事件侦听器函数中:

// Get all the clients, and for each post a message
clients.matchAll().then(clients => {
    clients.forEach(client => {
        // Post "addAll" to get a list of files to cache
        clientPostMessage(client, "addAll").then(message => {
            // For each file, check if it already exists in the cache
            message.forEach(url => {
                caches.match(url).then(result => {
                    // If there's nothing in the cache, fetch the file and cache it
                    if(!result) {
                        fetch(url).then(response => {
                            caches.open(cacheName).then(cache => {
                                cache.put(url, response);
                            });
                        });
                    }
                })
            });
        });
    })
});

对于所有客户端,服务工作者向页面发送“addAll”消息并获取结果。对于结果中的每个项目,它会检查该值是否已存在于缓存中,如果不存在,则提取并添加该值 使用此方法,服务工作者的安装侦听器只需包含:

self.addEventListener('install', event => {
    if(self.skipWaiting) {
        self.skipWaiting();
    }
    event.waitUntil(
        caches.open(cacheName).then(cache => {
            return cache.addAll([
                '/',
                '/index.html',
            ])
        })
    );
});

到目前为止似乎运作良好,如果有人有任何建议或看到任何错误我会很高兴听到!你也可以告诉我这有多么不合适,但它使我的生活更容易为依赖于未捆绑在一起的脚本的预先存在的项目添加服务工作者。