如何使用单个 Javascript 标签同时安装服务工作者和清单?

How do I install both a service worker and a manifest with a single Javascript tag?

我已经为网站构建了一项服务,可以轻松地将推送通知集成到他们的网站中,但当前的 Chrome 实施还需要他们设置清单和服务工作者。

有什么方法可以只用一行 Javascript 来设置 service worker 和 manifest 吗?

是的,您可以减少让他们在其来源上托管服务工作者文件的步骤,并在他们的页面中包含一行 Javascript。

您可以使用它们包含的 Javascript 来做到这一点:

  1. 注册服务工作者
  2. 将 link 注入一个不存在的清单到头部
  3. 拦截对该清单的请求 与您的服务人员联系并回复您的自定义清单

您的 Javascript 应如下所示:

navigator.serviceWorker.register('sw.js').then(function(registration) {
    console.log('ServiceWorker registration successful with scope: ', registration.scope);
    var head = document.head;
    var noManifest = true;
    // Walk through the head to check if a manifest already exists
    for (var i = 0; i < head.childNodes.length; i++) {
        if (head.childNodes[i].rel === 'manifest') {
            noManifest = false;
            break;
        }
    }
    // If there is no manifest already, add one.
    if (noManifest) {
        var manifest = document.createElement('link');
        manifest.rel = 'manifest';
        manifest.href = 'manifest.json';
        document.head.appendChild(manifest);
    }
});

请注意我是如何避免添加清单标签的(如果清单标签已经存在)。

您的 Service Worker 应该类似于:

self.addEventListener('fetch', function(e) {
    if (e.request.context === 'manifest') {
        e.respondWith(new Promise(function(resolve, reject) {
            fetch(e.request).then(function(response) {
                if (response.ok) {
                    // We found a real manifest, so we should just add our custom field
                    response.json().then(function(json) {
                        json.custom_field = 'Hello world';
                        var blob = new Blob([JSON.stringify(json)], { type: 'application/json' });
                        console.log('Appended a custom field to the pre-existing manifest');
                        resolve(new Response(blob));
                    });
                } else {
                    // There was no manifest so return ours
                    console.log('Injected a custom manifest');
                    resolve(new Response('{ "custom_field": "Hello world" }'));
                }
            });
        }));
    }
});

// These pieces cause the service worker to claim the client immediately when it is registered instead of waiting until the next load. This means this approach can work immediately when the user lands on your site.
if (typeof self.skipWaiting === 'function') {
    console.log('self.skipWaiting() is supported.');
    self.addEventListener('install', function(e) {
        // See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-global-scope-skipwaiting
        e.waitUntil(self.skipWaiting());
    });
} else {
    console.log('self.skipWaiting() is not supported.');
}

if (self.clients && (typeof self.clients.claim === 'function')) {
    console.log('self.clients.claim() is supported.');
    self.addEventListener('activate', function(e) {
        // See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#clients-claim-method
        e.waitUntil(self.clients.claim());
    });
} else {
    console.log('self.clients.claim() is not supported.');
}

请注意它如何只拦截对清单的请求,并检查清单是否确实存在。如果是,service worker 只需将我们的自定义字段附加到它。如果清单尚不存在,我们 return 我们的自定义字段作为整个清单。

更新(2015 年 8 月 25 日):我刚刚读到 Request.context has been deprecated 所以不幸的是,您需要找到一种新方法来发现请求是针对清单还是其他内容来代替行 if (e.request.context === 'manifest').