У меня есть PWA, созданный с помощью Aurelia и скомпилированный с помощью Webpack с использованием плагина Workbox, который генерирует рабочий файл службы sw.js
. Я пытаюсь сделать уведомление пользователя «Доступна новая версия», чтобы пользователь мог активировать новую версию при нажатии на ссылку в приложении.
Я успешно загружаю и устанавливаю новую версию в фоновом режиме и даже обнаруживаю, что новая версия готова. Однако, когда я пытаюсь вызвать метод skipWaiting()
для принудительного обновления страницы с новой версией, это не удается, потому что, по-видимому, у меня нет нужной области или объекта.
Основная проблема, вероятно, в том, что я не могу редактировать фактический sw.js, потому что он создается автоматически. Все примеры предполагают использование self.skipWaiting();
, но я не знаю, как получить доступ к этому объекту.
webpack.config.js
new WorkboxPlugin({
globDirectory: './dist',
globPatterns: ['**/*.{html,js,css,woff,woff2,ttf,svg,eot,jpg}'],
swDest: './dist/sw.js',
clientsClaim: true,
skipWaiting: false, // because I want to notify the user and wait for response
}),
index.ejs
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => {
// make the registration available globally, for access within app
window.myServiceWorkerReg = reg;
// Check for update on loading the app (is this necessary?)
return reg.update();
})
.catch(console.error);
}
</script>
app.js
activate() {
// listener for service worker update
this.swReg = window.myServiceWorkerReg;
console.warn('[app.js] ACTIVATE.', this.swReg);
this.swReg.addEventListener('updatefound', () => {
// updated service worker found in reg.installing!
console.warn('[app.js] UPDATE FOUND.', this.swReg);
const newWorker = this.swReg.installing;
newWorker.addEventListener('statechange', () => {
// has the service worker state changed?
console.warn('[app.js] STATE HAS CHANGED.', newWorker, newWorker.state);
if (newWorker.state === 'installed') {
// New service worker ready.
// Notify user; callback for user request to load new app
myUserMessage({ clickToActivate: () => {
// reload fresh copy (do not cache)
console.warn('[app.js] Post Action: skipWaiting.');
// this.swReg.postMessage({ action: 'skipWaiting' });
// THIS IS THE LINE THAT FAILS
this.swReg.skipWaiting();
}});
}
});
});
}
Все работает нормально, кроме последней строки (this.swReg.skipWaiting();
). Кто-нибудь еще использовал плагин webpack + workbox и получил skipWaiting в результате взаимодействия с пользователем?
🤔 А знаете ли вы, что...
Aurelia позволяет использовать различные CSS-препроцессоры, такие как LESS и SASS.
Вы не можете вызвать его на странице (app.js). Вы вызываете self.skipWaiting в сценарии Service Worker (service-worker.js).
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting
Наконец-то я заработал. Одна проблема заключалась в том, что я использовал более старую версию workbox-webpack-plugin. Текущая версия (4.2) включает прослушиватель в сервис-воркере, который может запускать self.skipWaiting()
, когда сообщение отправляется работнику следующим образом:
newWorker.postMessage({ type: 'SKIP_WAITING' });
Но вы должны убедиться, что в конфигурации есть skipWaiting: false;
и что вы используете последнюю версию.
Эти инструкции довольно хороши:
https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin
Тем не менее, я настроил вещи, чтобы они хорошо работали между моим приложением и экземпляром работника службы в файле index.ejs.
webpack.config.js
new GenerateSW({
globPatterns: ['dist/**/*.{html,js,css,woff,woff2,ttf,svg,eot,jpg}'],
swDest: 'sw.js',
clientsClaim: true,
skipWaiting: false,
})),
index.ejs
<script>
if ('serviceWorker' in navigator) {
// register the service worker
navigator.serviceWorker.register('/sw.js')
.then(reg => {
window.myWorkerReg = reg;
// Check for update on loading the app (is this necessary?)
return reg.update();
})
.catch(console.error);
// The event listener that is fired when the service worker updates
navigator.serviceWorker.addEventListener('controllerchange', function () {
// when the service worker controller is changed, reload the page
if (window.swRefreshing) return;
window.location.reload();
window.swRefreshing = true;
});
}
</script>
app.js
activate() {
// listener for service worker update
this.swReg = window.myWorkerReg;
if (this.swReg) {
// if there is already a new service worker ready to install, prompt user
if (this.swReg.waiting) {
this.promptUpdateServiceWorker(this.swReg.waiting);
}
// add listener to detect when a new service worker is downloaded
this.swReg.addEventListener('updatefound', () => {
// updated service worker is being installed
const newWorker = this.swReg.installing;
// add listener to detect when installation is finished
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
// New service worker ready to activate; prompt user
this.promptUpdateServiceWorker(newWorker);
}
});
});
}
}
// listener for buildVersion
buildVersionChanged(buildVersion) {
// through proprietary code, we've detected a new version could be downloaded now
window.myWorkerReg.update();
}
// New service worker ready. Show the notification
promptUpdateServiceWorker(newWorker) {
// actual code for UI prompt will vary; this is pseudocode
uiPrompt('New_version_ready').then((response) => {
if (response.approved) {
// reload fresh copy (do not cache)
newWorker.postMessage({ type: 'SKIP_WAITING' });
}
});
}