Доступ к домену веб-работником

Я схожу с ума и нуждаюсь в вашей помощи. Я работаю над проектом serviceworker, и я имею дело с проблемой javascript. У меня есть два основных файла. Файл server.html, в который я вызываю внешний service-worker.js файл в строке 52. Вот мой server.html файл

<body>
    <div class="container"> 
        <h1>PRESENTER</h1>
        <div id="nicky">Nickname: <span id="nickname"></span></div>
        <form id="form-nick" name="form-nick" method="post" action="">
            <div class="formelement">
                <label name="labelnick" for="nick">Nickname:</label>
                <input type="text" id="nick" name="nick">
                <button type="submit">OK</button>
            </div>
        </form><br /><br />

        <h1>--></h1><div id="talker"></div>

        <button type="button" class="button blue" id="blue-display" disabled></button><br />

        <button type="button" class="button red" disabled></button><br />

        <button type="button" class="button lightblue" disabled></button>
    </div> <!-- container -->


    <script type="text/javascript">
        $(document).ready(function() {
            console.log("jquery ready function");

            $('#nick').focus();

            $('#form-nick').submit(function(){
                var form = $('#form-nick');
                var data = form.serialize();
                $.post('nicky.php', data, function(response) {
                    if (response) {
                        $('#nicky').show();
                        $('#nickname').text(response);
                        $('#form-nick').hide();
                        $('.blue, .red, .lightblue').fadeIn(100);

                        if('serviceWorker' in navigator){
                            // Register service worker
                            navigator.serviceWorker.register('service-worker.js').then(function(reg){
                                console.log("SW registration succeeded. Scope is "+reg.scope);

                            }).catch(function(err){
                                console.error("SW registration failed with error "+err);
                            });
                        }
                    } else {

                    }
                });
                return false;           
            });       
        });
    </script>
</body>

и вот файл service-worker.js

// Install Service Worker
self.addEventListener('install', function(event){
    console.log('>> sw installed!');
});
// Service Worker Active
self.addEventListener('activate', function(event){
    console.log('>> sw activated!');
});
// Service Worker reveives message
self.addEventListener('message', function(event){
    console.log(event.data);
    send_message_to_all_clients(event.data);
    document.getElementById("talker").innerHTML = event.data;
});

В последней строке я хотел бы вставить полученное сообщение в div-talker. Но я всегда получаю сообщение об ошибке service-worker.js: 17 Uncaught ReferenceError: документ не определен

Я позаботился о том, чтобы загрузить js файл после загрузки документа. Теперь я не знаю, что я делаю неправильно. Благодарю.

Ответ 1

Рабочие службы — веб-работники вообще — не имеют прямого доступа к DOM. Вместо этого, рабочий должен опубликовать информацию в основной поток, а код в основном потоке обновит DOM, если это необходимо. Замещающая модель для JavaScript в браузерах заключается в том, что существует только один основной поток пользовательского интерфейса (по умолчанию используется ваш код на странице), который может получить доступ к DOM. Остальные отгорожены от него.

Эта страница и эта страница говорят о обмене сообщениями между сервисными работниками и клиентами. Вот действительно простой пример:

Script на странице загрузки сервисного работника:

(function() {
    "use strict";

    if (!navigator.serviceWorker || !navigator.serviceWorker.register) {
        console.log("This browser doesn't support service workers");
        return;
    }

    // Listen to messages from service workers.
    navigator.serviceWorker.addEventListener('message', function(event) {
        console.log("Got reply from service worker: " + event.data);
    });

    // Are we being controlled?
    if (navigator.serviceWorker.controller) {
        // Yes, send our controller a message.
        console.log("Sending 'hi' to controller");
        navigator.serviceWorker.controller.postMessage("hi");
    } else {
        // No, register a service worker to control pages like us.
        // Note that it won't control this instance of this page, it only takes effect
        // for pages in its scope loaded *after* it installed.
        navigator.serviceWorker.register("service-worker.js")
            .then(function(registration) {
                console.log("Service worker registered, scope: " + registration.scope);
                console.log("Refresh the page to talk to it.");
                // If we want to, we might do `location.reload();` so that we'd be controlled by it
            })
            .catch(function(error) {
                console.log("Service worker registration failed: " + error.message);
            });
    }
})();

И в service-worker.js:

self.addEventListener("message", function(event) {
    event.source.postMessage("Responding to " + event.data);
});

Это зависит от event.source, который поддерживается текущими версиями Chrome и Firefox.

В качестве альтернативы вместо event.source вы можете отправить сообщение нескольким клиентам сервисного работника, используя self.clients.matchAll; снова в service-worker.js:

self.addEventListener("message", function(event) {
    self.clients.matchAll().then(all => all.forEach(client => {
        client.postMessage("Responding to " + event.data);
    }));
});

matchAll принимает некоторые параметры фильтрации.


Вы сказали, что у вас проблемы с работой. Здесь полная версия того, что работает для меня в Chrome и Firefox:

service-worker.html:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Service Worker</title>
</head>
<body>
(Look in the console.)
<script>
(function() {
    "use strict";

    if (!navigator.serviceWorker || !navigator.serviceWorker.register) {
        console.log("This browser doesn't support service workers");
        return;
    }

    // Listen to messages from service workers.
    navigator.serviceWorker.addEventListener('message', function(event) {
        console.log("Got reply from service worker: " + event.data);
    });

    // Are we being controlled?
    if (navigator.serviceWorker.controller) {
        // Yes, send our controller a message.
        console.log("Sending 'hi' to controller");
        navigator.serviceWorker.controller.postMessage("hi");
    } else {
        // No, register a service worker to control pages like us.
        // Note that it won't control this instance of this page, it only takes effect
        // for pages in its scope loaded *after* it installed.
        navigator.serviceWorker.register("service-worker.js")
            .then(function(registration) {
                console.log("Service worker registered, scope: " + registration.scope);
                console.log("Refresh the page to talk to it.");
                // If we want to, we might do `location.reload();` so that we'd be controlled by it
            })
            .catch(function(error) {
                console.log("Service worker registration failed: " + error.message);
            });
    }
})();
</script>
</body>
</html>

service-worker.js:

self.addEventListener("message", function(event) {
    //event.source.postMessage("Responding to " + event.data);
    self.clients.matchAll().then(all => all.forEach(client => {
        client.postMessage("Responding to " + event.data);
    }));
});

Как вы можете видеть, эта версия использует self.clients.matchAll, с прокомментированной версией event.source над ней.

Если я запустил это в двух окнах, каждое обновление каждого окна отправит сообщение другим окнам (потому что я использую self.clients.matchAll...).

Ответ 2

Служебный работник основан на асинхронном режиме, и согласно статье вы не можете получить доступ к DOM, используя сервис, который работал для получения дополнительной информации. https://developers.google.com/web/fundamentals/primers/service-workers/

Ответ 3

@TJ Crowder:

Я предпринял попытку адаптировать ваш код сообщения ^^^ в приложении Electronjs, которое загружает и кэширует внешний веб-сайт, потому что, по-видимому, электронная служба не поддерживает уведомления сервисного работника (sw).

Я пытаюсь сгенерировать уведомление, когда кэширование sw завершено и активирован sw.

index.html загрузка встроенного скрипта sw:

navigator.serviceWorker.addEventListener('message', function(event) {
    console.log("from sw: " + event.data);
});

my-serviceworker.js (соответствующая часть):

global.addEventListener('activate, message', event => {
  event.waitUntil(async function() {
    await global.clients.claim();
    console.log('cache (re)loaded, to dom: ' + event.data);
    event.source.postMessage(event.data);
  }());
});

Хотя это не приводит к возникновению каких-либо ошибок в консоли, и в противном случае sw по-прежнему функционирует должным образом, он также не регистрирует в файле event.data сценарий.

Любые идеи будут с благодарностью!

Спасибо