使用 firebase 托管和功能的 Service Worker 注册失败
Service Worker registration fails with firebase hosting and functions
Service Worker 未在由 Nuxt.js(通用)构建的应用程序中注册,该应用程序 运行ning on Firebase Functions。
我目前正在尝试使用 Nuxt.js 通用模式和 Firebase 函数构建原型 Web 应用程序。
该应用程序使用nuxt官方pwa-module的service worker来管理登录用户的session,以便客户端可以向服务器发送具有授权header的请求,服务器将验证用户session 通过 Firebase 身份验证。
我已经尝试使用 yarn run build
和 yarn run start
在本地服务器上 运行 它,并确保 service worker 已正确注册并完美运行。
但是,当我尝试使用 firebase serve
检查相同的操作时,我在浏览器上收到以下错误。
A bad HTTP response code (404) was received when fetching the script.
Failed to load resource: net::ERR_INVALID_RESPONSE
dcbac67bb39a765db27a.js:1 Service worker registration failed: TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.
我还发现 service worker 没有将 session 数据发送到服务器。当我使用 firebase deploy
.
将源代码部署到生产中时,复制了完全相同的内容
甚至在我使用 yarn create nuxt-app
、yarn run build
和 firebase serve
完全清理项目时也会发生这种情况。 "sw.js" 文件指示 Chrome 开发者工具的失败状态。
我不确定它是否与我有关,但我的 Firebase 使用 Spark 计划。
我的项目树。
.
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── functions
│ ├── index.js
│ ├── nuxt
│ │ ├── App.js
│ │ ├── axios.js
│ │ ├── client.js
│ │ ├── components
│ │ ├── dist
│ │ ├── empty.js
│ │ ├── index.js
│ │ ├── loading.html
│ │ ├── middleware.js
│ │ ├── router.js
│ │ ├── server.js
│ │ ├── store.js
│ │ ├── sw.plugin.js
│ │ ├── sw.template.js
│ │ ├── utils.js
│ │ └── views
│ ├── package-lock.json
│ ├── package.json
│ └── yarn.lock
├── public
└── src
├── assets
├── components
├── jest.config.js
├── layouts
├── middleware
├── nuxt.config.js
├── package.json
├── pages
├── plugins
├── server
├── static
│ └── sw.js
├── store
└── test
└── yarn.lock
src/static/sw.js
importScripts('/_nuxt/workbox.4c4f5ca6.js')
workbox.precaching.precacheAndRoute([
{
"url": "/_nuxt/021e1640b53136b75c48.js",
"revision": "2753e747206d803c793b59a324c1931b"
},
{
"url": "/_nuxt/03c9e340d8692d1403a9.js",
"revision": "2b882d73d20a0b2cfd318e9e05d3496e"
},
{
"url": "/_nuxt/78bddfa6b6a4919b78d6.js",
"revision": "70312f6623089e3b4aa6454120c1e177"
},
{
"url": "/_nuxt/85c817abccdd40162004.js",
"revision": "45448f8709d01af59bb125a1d06be23a"
},
{
"url": "/_nuxt/8654a518d0f3e326c9a6.js",
"revision": "a42426f5e7b458acc6fdf0e9e48c7d35"
},
{
"url": "/_nuxt/95b421159066eff9e318.js",
"revision": "efd1643042f804defa7212979867558a"
},
{
"url": "/_nuxt/9b487bf2df1190b68565.js",
"revision": "78837d0e624deccc85761b44f9ede9be"
},
{
"url": "/_nuxt/ce59381752309c170d41.js",
"revision": "d3e50bf27891c4efa6dff5076a0772a6"
},
{
"url": "/_nuxt/d8fa6ae5ca14331879f6.js",
"revision": "5da6698c9359df803243ecc44519b610"
},
{
"url": "/_nuxt/dcbac67bb39a765db27a.js",
"revision": "9505aa84ddb2a70b826d324433daff36"
},
{
"url": "/_nuxt/dee89eef613849499c7f.js",
"revision": "43117ed819efe3d00c963dd655894d5f"
}
], {
"cacheId": "justtest",
"directoryIndex": "/",
"cleanUrls": false
})
workbox.clientsClaim()
workbox.skipWaiting()
workbox.routing.registerRoute(new RegExp('/_nuxt/.*'), workbox.strategies.cacheFirst({}), 'GET')
workbox.routing.registerRoute(new RegExp('/.*'), workbox.strategies.networkFirst({}), 'GET')
functions/index.js
const functions = require("firebase-functions")
const { Nuxt } = require("nuxt")
const express = require("express")
const app = express()
const nuxt = new Nuxt({ buildDir: "nuxt", dev: false })
function handleRequest(req, res) {
res.setHeader('Cache-Control', 'private')
return new Promise((resolve, reject) => {
nuxt.render(req, res, promise => {
promise.then(resolve).catch(reject)
})
})
}
app.use(handleRequest)
exports.ssr = functions.https.onRequest(app)
预期的行为是 sw.js 当我 运行 使用 firebase serve
和 firebase deploy
的应用程序时工作正常。
我设法找到了解决方案。我可能误解了服务人员的行为。
我只是留下我的解决方案,以便我可以帮助有同样情况的人。
首先,我根本不知道服务工作者 js 文件 /static/sw.js
,因为它是由 yarn run build
自动创建的,当我 运行 应用程序时一切正常yarn run start
。因此我认为我根本不必关心那个文件。
但是我发现/static
目录中的所有文件都必须放在/public
目录中,该目录将由firebase托管,以便客户端可以找到并下载它们。我想当我使用 yarn run start
时,/static
目录中的文件将由开发服务器托管。
我还意识到服务工作者是静态 javascript 文件,它们必须由 firebase 托管而不是由 firebase 函数托管。我曾经尝试将它们放在 dist/client
中,但没有任何意义。它必须放在 /public
目录中。
我终于让它与以下项目树一起工作。当我 运行 yarn run build
我将 /src/static
中的所有内容复制到 /public
以使用 firebase 托管它们。
.
├── firebase.json
├── functions
│ ├── index.js
│ ├── nuxt
│ │ ├── App.js
│ │ ├── axios.js
│ │ ├── client.js
│ │ ├── components
│ │ ├── dist
│ │ ├── empty.js
│ │ ├── index.js
│ │ ├── loading.html
│ │ ├── middleware.js
│ │ ├── router.js
│ │ ├── server.js
│ │ ├── store.js
│ │ ├── sw.plugin.js
│ │ ├── sw.template.js
│ │ ├── utils.js
│ │ └── views
│ ├── package-lock.json
│ ├── package.json
│ └── yarn.lock
├── public
│ ├── favicon.ico
│ ├── sw-firebase-auth.js
│ └── sw.js
└── src
├── assets
├── components
├── layouts
├── middleware
├── nuxt.config.js
├── package.json
├── pages
├── plugins
├── server
├── static
│ ├── favicon.ico
│ ├── sw-firebase-auth.js
│ └── sw.js
├── store
└── yarn.lock
这是我的sw-firebase-auth.js
。 (我用 Browserify 编译这个脚本来工作。)
var firebase = require('firebase')
// Initialize the Firebase app in the service worker script.
firebase.initializeApp({
apiKey: '*************',
authDomain: '*************',
databaseURL: '*************',
projectId: '*************',
storageBucket: '*************',
messagingSenderId: '*************'
})
/**
* Returns a promise that resolves with an ID token if available.
* @return {!Promise<?string>} The promise that resolves with an ID token if
* available. Otherwise, the promise resolves with null.
*/
const getIdToken = () => {
return new Promise((resolve) => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
unsubscribe();
if (user) {
user.getIdToken().then((idToken) => {
resolve(idToken)
}, () => {
resolve(null)
});
} else {
resolve(null)
}
})
})
}
const getOriginFromUrl = (url) => {
const pathArray = url.split('/');
const protocol = pathArray[0];
const host = pathArray[2];
return protocol + '//' + host;
};
self.addEventListener('fetch', (event) => {
const requestProcessor = (idToken) => {
let req = event.request;
if (self.location.origin == getOriginFromUrl(event.request.url) &&
(self.location.protocol == 'https:' ||
self.location.hostname == 'localhost') &&
idToken) {
const headers = new Headers();
for (let entry of req.headers.entries()) {
headers.append(entry[0], entry[1]);
}
headers.append('Authorization', 'Bearer ' + idToken);
try {
req = new Request(req.url, {
method: req.method,
headers: headers,
mode: 'same-origin',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
body: req.body,
bodyUsed: req.bodyUsed,
context: req.context
});
} catch (e) {
console.log(e)
}
}
return fetch(req);
};
event.respondWith(getIdToken().then(requestProcessor, requestProcessor));
});
self.addEventListener('activate', event => {
event.waitUntil(clients.claim());
})
这也是 nuxt.conf.js
中的工作箱配置。它与使用 importScripts
.
的官方 pwa 模块配合得很好
workbox: {
importScripts: [
'sw-firebase-auth.js'
]
}
我希望这会对某人有所帮助。
各位 Nuxt.js 开发人员 2020 年 6 月
如果您使用 SSR 部署 Nuxt.js 2.12.2 项目,您的项目目录中可能有 .nuxt
文件夹。删除它,根据需要从 nuxt.config.js
和 运行 nuxt build
、npm run build
或 yarn build
配置 Workbox。
如果它在本地计算机上工作,您可以尝试清除服务器上的缓存以确保所有旧的 .nuxt
文件夹都消失并重新部署项目。
希望对你有帮助✨
Service Worker 未在由 Nuxt.js(通用)构建的应用程序中注册,该应用程序 运行ning on Firebase Functions。
我目前正在尝试使用 Nuxt.js 通用模式和 Firebase 函数构建原型 Web 应用程序。 该应用程序使用nuxt官方pwa-module的service worker来管理登录用户的session,以便客户端可以向服务器发送具有授权header的请求,服务器将验证用户session 通过 Firebase 身份验证。
我已经尝试使用 yarn run build
和 yarn run start
在本地服务器上 运行 它,并确保 service worker 已正确注册并完美运行。
但是,当我尝试使用 firebase serve
检查相同的操作时,我在浏览器上收到以下错误。
A bad HTTP response code (404) was received when fetching the script.
Failed to load resource: net::ERR_INVALID_RESPONSE
dcbac67bb39a765db27a.js:1 Service worker registration failed: TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.
我还发现 service worker 没有将 session 数据发送到服务器。当我使用 firebase deploy
.
甚至在我使用 yarn create nuxt-app
、yarn run build
和 firebase serve
完全清理项目时也会发生这种情况。 "sw.js" 文件指示 Chrome 开发者工具的失败状态。
我不确定它是否与我有关,但我的 Firebase 使用 Spark 计划。
我的项目树。
.
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── functions
│ ├── index.js
│ ├── nuxt
│ │ ├── App.js
│ │ ├── axios.js
│ │ ├── client.js
│ │ ├── components
│ │ ├── dist
│ │ ├── empty.js
│ │ ├── index.js
│ │ ├── loading.html
│ │ ├── middleware.js
│ │ ├── router.js
│ │ ├── server.js
│ │ ├── store.js
│ │ ├── sw.plugin.js
│ │ ├── sw.template.js
│ │ ├── utils.js
│ │ └── views
│ ├── package-lock.json
│ ├── package.json
│ └── yarn.lock
├── public
└── src
├── assets
├── components
├── jest.config.js
├── layouts
├── middleware
├── nuxt.config.js
├── package.json
├── pages
├── plugins
├── server
├── static
│ └── sw.js
├── store
└── test
└── yarn.lock
src/static/sw.js
importScripts('/_nuxt/workbox.4c4f5ca6.js')
workbox.precaching.precacheAndRoute([
{
"url": "/_nuxt/021e1640b53136b75c48.js",
"revision": "2753e747206d803c793b59a324c1931b"
},
{
"url": "/_nuxt/03c9e340d8692d1403a9.js",
"revision": "2b882d73d20a0b2cfd318e9e05d3496e"
},
{
"url": "/_nuxt/78bddfa6b6a4919b78d6.js",
"revision": "70312f6623089e3b4aa6454120c1e177"
},
{
"url": "/_nuxt/85c817abccdd40162004.js",
"revision": "45448f8709d01af59bb125a1d06be23a"
},
{
"url": "/_nuxt/8654a518d0f3e326c9a6.js",
"revision": "a42426f5e7b458acc6fdf0e9e48c7d35"
},
{
"url": "/_nuxt/95b421159066eff9e318.js",
"revision": "efd1643042f804defa7212979867558a"
},
{
"url": "/_nuxt/9b487bf2df1190b68565.js",
"revision": "78837d0e624deccc85761b44f9ede9be"
},
{
"url": "/_nuxt/ce59381752309c170d41.js",
"revision": "d3e50bf27891c4efa6dff5076a0772a6"
},
{
"url": "/_nuxt/d8fa6ae5ca14331879f6.js",
"revision": "5da6698c9359df803243ecc44519b610"
},
{
"url": "/_nuxt/dcbac67bb39a765db27a.js",
"revision": "9505aa84ddb2a70b826d324433daff36"
},
{
"url": "/_nuxt/dee89eef613849499c7f.js",
"revision": "43117ed819efe3d00c963dd655894d5f"
}
], {
"cacheId": "justtest",
"directoryIndex": "/",
"cleanUrls": false
})
workbox.clientsClaim()
workbox.skipWaiting()
workbox.routing.registerRoute(new RegExp('/_nuxt/.*'), workbox.strategies.cacheFirst({}), 'GET')
workbox.routing.registerRoute(new RegExp('/.*'), workbox.strategies.networkFirst({}), 'GET')
functions/index.js
const functions = require("firebase-functions")
const { Nuxt } = require("nuxt")
const express = require("express")
const app = express()
const nuxt = new Nuxt({ buildDir: "nuxt", dev: false })
function handleRequest(req, res) {
res.setHeader('Cache-Control', 'private')
return new Promise((resolve, reject) => {
nuxt.render(req, res, promise => {
promise.then(resolve).catch(reject)
})
})
}
app.use(handleRequest)
exports.ssr = functions.https.onRequest(app)
预期的行为是 sw.js 当我 运行 使用 firebase serve
和 firebase deploy
的应用程序时工作正常。
我设法找到了解决方案。我可能误解了服务人员的行为。
我只是留下我的解决方案,以便我可以帮助有同样情况的人。
首先,我根本不知道服务工作者 js 文件 /static/sw.js
,因为它是由 yarn run build
自动创建的,当我 运行 应用程序时一切正常yarn run start
。因此我认为我根本不必关心那个文件。
但是我发现/static
目录中的所有文件都必须放在/public
目录中,该目录将由firebase托管,以便客户端可以找到并下载它们。我想当我使用 yarn run start
时,/static
目录中的文件将由开发服务器托管。
我还意识到服务工作者是静态 javascript 文件,它们必须由 firebase 托管而不是由 firebase 函数托管。我曾经尝试将它们放在 dist/client
中,但没有任何意义。它必须放在 /public
目录中。
我终于让它与以下项目树一起工作。当我 运行 yarn run build
我将 /src/static
中的所有内容复制到 /public
以使用 firebase 托管它们。
.
├── firebase.json
├── functions
│ ├── index.js
│ ├── nuxt
│ │ ├── App.js
│ │ ├── axios.js
│ │ ├── client.js
│ │ ├── components
│ │ ├── dist
│ │ ├── empty.js
│ │ ├── index.js
│ │ ├── loading.html
│ │ ├── middleware.js
│ │ ├── router.js
│ │ ├── server.js
│ │ ├── store.js
│ │ ├── sw.plugin.js
│ │ ├── sw.template.js
│ │ ├── utils.js
│ │ └── views
│ ├── package-lock.json
│ ├── package.json
│ └── yarn.lock
├── public
│ ├── favicon.ico
│ ├── sw-firebase-auth.js
│ └── sw.js
└── src
├── assets
├── components
├── layouts
├── middleware
├── nuxt.config.js
├── package.json
├── pages
├── plugins
├── server
├── static
│ ├── favicon.ico
│ ├── sw-firebase-auth.js
│ └── sw.js
├── store
└── yarn.lock
这是我的sw-firebase-auth.js
。 (我用 Browserify 编译这个脚本来工作。)
var firebase = require('firebase')
// Initialize the Firebase app in the service worker script.
firebase.initializeApp({
apiKey: '*************',
authDomain: '*************',
databaseURL: '*************',
projectId: '*************',
storageBucket: '*************',
messagingSenderId: '*************'
})
/**
* Returns a promise that resolves with an ID token if available.
* @return {!Promise<?string>} The promise that resolves with an ID token if
* available. Otherwise, the promise resolves with null.
*/
const getIdToken = () => {
return new Promise((resolve) => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
unsubscribe();
if (user) {
user.getIdToken().then((idToken) => {
resolve(idToken)
}, () => {
resolve(null)
});
} else {
resolve(null)
}
})
})
}
const getOriginFromUrl = (url) => {
const pathArray = url.split('/');
const protocol = pathArray[0];
const host = pathArray[2];
return protocol + '//' + host;
};
self.addEventListener('fetch', (event) => {
const requestProcessor = (idToken) => {
let req = event.request;
if (self.location.origin == getOriginFromUrl(event.request.url) &&
(self.location.protocol == 'https:' ||
self.location.hostname == 'localhost') &&
idToken) {
const headers = new Headers();
for (let entry of req.headers.entries()) {
headers.append(entry[0], entry[1]);
}
headers.append('Authorization', 'Bearer ' + idToken);
try {
req = new Request(req.url, {
method: req.method,
headers: headers,
mode: 'same-origin',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
body: req.body,
bodyUsed: req.bodyUsed,
context: req.context
});
} catch (e) {
console.log(e)
}
}
return fetch(req);
};
event.respondWith(getIdToken().then(requestProcessor, requestProcessor));
});
self.addEventListener('activate', event => {
event.waitUntil(clients.claim());
})
这也是 nuxt.conf.js
中的工作箱配置。它与使用 importScripts
.
workbox: {
importScripts: [
'sw-firebase-auth.js'
]
}
我希望这会对某人有所帮助。
各位 Nuxt.js 开发人员 2020 年 6 月
如果您使用 SSR 部署 Nuxt.js 2.12.2 项目,您的项目目录中可能有 .nuxt
文件夹。删除它,根据需要从 nuxt.config.js
和 运行 nuxt build
、npm run build
或 yarn build
配置 Workbox。
如果它在本地计算机上工作,您可以尝试清除服务器上的缓存以确保所有旧的 .nuxt
文件夹都消失并重新部署项目。
希望对你有帮助✨