ServiceWorker 更新循环 - 创建 React 应用程序
ServiceWorker Update loop - Create react app
我正在尝试为用户实现一个弹出窗口,告诉他们有更新可用。
我正在使用 Create React App,它是默认配置。
当它发现像这样的更新时,我正在调度一个动作:
index.js
serviceWorker.register({
onSuccess: () => store.dispatch({ type: SW_UPDATE_FINISHED }),
onUpdate: reg => store.dispatch({ type: SW_UPDATE, payload: reg })
});
我有一个小组件应该负责显示消息和触发更新:
const SwUpdate = () => {
const swUpdate = useSelector(state => state.app.swUpdate);
const swReg = useSelector(state => state.app.swReg);
const dispatch = useDispatch();
const updateSw = () => {
const swWaiting = swReg && swReg.waiting;
if (swWaiting) {
swWaiting.postMessage({ type: 'SKIP_WAITING' });
dispatch({ type: SW_UPDATE_FINISHED });
window.location.reload();
} else {
dispatch({ type: SW_UPDATE_FINISHED });
}
}
return (
<Snackbar
open={swUpdate}
color="primary"
message="New version available! "
action={
<Button color="primary" size="small" onClick={updateSw}>
UPDATE
</Button>
}
/>
)
}
export default SwUpdate;
它做了我认为应该做的事情,重新加载页面...
但是...
再次...您有更新!每次从检查员那里手动完成都是同样的故事,我得到一个新的软件,但我不明白为什么会这样
我似乎找不到发生在其他人身上的例子,我担心是我做错了什么,还是 CRA 服务人员有问题,我只将回调添加到寄存器的配置中参数并在特定组件上对其进行管理。
已编辑:添加服务人员代码
我有来自 Create React App 的 serviceWorker.js
原样:
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets;
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit <LINK>'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See '
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
我知道他们使用 Workbox 进行配置,我正在通过 运行 npm run build
和 serve -s build
测试所有这些并重新加载以进行尝试。
此外,这里是Workbox在构建应用时配置Create react app生成的service-worker文件:
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.a4724df64b745797c25a9173550ba2d3.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
});
显然,Firebase 消息推送通知需要另一个 service-worker 每次都会触发更新。
老实说,我没有在原来的问题中提到它,因为我认为它与我的问题没有任何关系。
我最终将 FCM 设置为使用我的 service worker 而不是拥有它,并且一切都开始正常工作了:)
我不能说更多,因为我不是 100% 确定真正的问题是什么,可能是 FCM serviceworker 没有更新自己,而是让另一个 serviceworker 每次都尝试更新自己?服务人员很奇怪
我遇到了完全相同的问题,每当更新弹出窗口时都会显示更新页面,并且新的服务人员将处于等待状态。我认为问题出在复选框 Update on Reload
(在“应用程序”选项卡上)被选中。一旦我关闭它,一切似乎都按预期工作。
我正在尝试为用户实现一个弹出窗口,告诉他们有更新可用。
我正在使用 Create React App,它是默认配置。 当它发现像这样的更新时,我正在调度一个动作:
index.js
serviceWorker.register({
onSuccess: () => store.dispatch({ type: SW_UPDATE_FINISHED }),
onUpdate: reg => store.dispatch({ type: SW_UPDATE, payload: reg })
});
我有一个小组件应该负责显示消息和触发更新:
const SwUpdate = () => {
const swUpdate = useSelector(state => state.app.swUpdate);
const swReg = useSelector(state => state.app.swReg);
const dispatch = useDispatch();
const updateSw = () => {
const swWaiting = swReg && swReg.waiting;
if (swWaiting) {
swWaiting.postMessage({ type: 'SKIP_WAITING' });
dispatch({ type: SW_UPDATE_FINISHED });
window.location.reload();
} else {
dispatch({ type: SW_UPDATE_FINISHED });
}
}
return (
<Snackbar
open={swUpdate}
color="primary"
message="New version available! "
action={
<Button color="primary" size="small" onClick={updateSw}>
UPDATE
</Button>
}
/>
)
}
export default SwUpdate;
它做了我认为应该做的事情,重新加载页面... 但是...
再次...您有更新!每次从检查员那里手动完成都是同样的故事,我得到一个新的软件,但我不明白为什么会这样
我似乎找不到发生在其他人身上的例子,我担心是我做错了什么,还是 CRA 服务人员有问题,我只将回调添加到寄存器的配置中参数并在特定组件上对其进行管理。
已编辑:添加服务人员代码
我有来自 Create React App 的 serviceWorker.js
原样:
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets;
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit <LINK>'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See '
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
我知道他们使用 Workbox 进行配置,我正在通过 运行 npm run build
和 serve -s build
测试所有这些并重新加载以进行尝试。
此外,这里是Workbox在构建应用时配置Create react app生成的service-worker文件:
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.a4724df64b745797c25a9173550ba2d3.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
});
显然,Firebase 消息推送通知需要另一个 service-worker 每次都会触发更新。
老实说,我没有在原来的问题中提到它,因为我认为它与我的问题没有任何关系。
我最终将 FCM 设置为使用我的 service worker 而不是拥有它,并且一切都开始正常工作了:)
我不能说更多,因为我不是 100% 确定真正的问题是什么,可能是 FCM serviceworker 没有更新自己,而是让另一个 serviceworker 每次都尝试更新自己?服务人员很奇怪
我遇到了完全相同的问题,每当更新弹出窗口时都会显示更新页面,并且新的服务人员将处于等待状态。我认为问题出在复选框 Update on Reload
(在“应用程序”选项卡上)被选中。一旦我关闭它,一切似乎都按预期工作。