create-react-app を express サーバーで使用しています。
create-react-app
には、ローカル資産をキャッシュする事前構成済みのServiceWorkerがあります( https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making -a-progressive-web-app )。
サーバーで公開しようとしたときに発生した問題は、service-worker.js
ファイルは使用できましたが、ブラウザコンソールで登録しようとしたときにエラーが発生していました。
Firefoxでは、次のエラーが表示されました。
Service Workerの登録中のエラー:
TypeError:ServiceWorker script at https://my-domain.com/service-worker.js for scope https://my-domain.com/ でエラーが発生しましたインストール。
Chromeでは、よりわかりやすいエラーが表示されます。
Service Workerの登録中のエラー:DOMException:ServiceWorkerの登録に失敗しました:スクリプトにサポートされていないMIMEタイプ( 'text/html')があります。
案の定、開発者ツールの[ネットワーク]タブを見て、service-worker.js
ファイル、HTTPヘッダーに間違ったMIMEタイプがあることがわかります。
しかし、なぜMIMEタイプが間違っているのか理解できませんでしたか?
Expressサーバーアプリケーションには、index.html
にリダイレクトする1つのワイルドカード(アスタリスク/ *)ルートがあります。
// Load React App
// Serve HTML file for production
if (env.name === "production") {
app.get("*", function response(req, res) {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
}
これは非常に一般的なデザインパターンです。ただし、未知のテキストファイルに対する要求は最初はindex.html
にリダイレクトされるため、実際にはJavaScriptまたはSVGまたは他の種類のプレーンテキストファイルであっても"text/html"
のMIMEタイプで返されます。 。
私が見つけた解決策は、service-worker.js
に特定のルートを追加することでしたbeforeワイルドカードルート:
app.get("/service-worker.js", (req, res) => {
res.sendFile(path.resolve(__dirname, "public", "service-worker.js"));
});
app.get("*", function response(req, res) {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
これで、ブラウザーがservice-worker.js
を検索すると、Expressは正しいMIMEタイプでそれを返します。
(ワイルドカードの後にservice-worker.js
ルートを追加しようとすると、ワイルドカードルートが上書きされるため機能しません。)
ServiceWorkerがサポートするMIMEタイプは、「text/javascript」、application/javascript、およびapplication/x-javascriptです。 urサーバーファイルにアクセスして設定
response.writeHead(201, {
'Content-Type': 'application/javascript'
});
交換
_'/service-worker.js'
_
と
_'./service-worker.js'
_
(navigator.serviceWorker.register('/service-worker.js')
内)
同様の問題を解決しました。
Service Workerファイルが生成されていることを確認します(Chrome DevTools-> Sources-> Filesystem)。 Service Workerファイルがまったく生成されない場合は、このようなエラーが表示される場合があります(展開スクリプトを確認してください)。
反応:public/index.html
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('sw-cached-site.js', function() {
navigator.serviceWorker.register('service-worker.js', {
scope: '/',
});
});
}
window.process = { env: { NODE_ENV: 'production' } };
</script>
注:完全なアプリ/サイトpublic/sw-cached-site.js
const cacheName = 'v1';
self.addEventListener('activate', (e) =>{
e.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache => {
if(cache !== cacheName) {
return caches.delete(cache);
}
})
)
})
)
});
self.addEventListener('fetch', e => {
e.respondWith(
fetch(e.request)
.then(res => {
const resClone = res.clone();
caches
.open(cacheName)
.then(cache => {
cache.put(e.request, resClone);
});
return res;
}).catch(err => caches.match(e.request).then(res => res))
);
});
NodeJSで作業している場合、serviceWorkerファイルはパブリックフォルダーに配置する必要があります。 Webpackを使用してserviceWorkerをパブリックにエクスポートする場合は、「copy-webpack-plugin」を使用する必要があります。
このスクリプトには、サポートされていないMIMEタイプ( 'text/html')があります。リソースの読み込みに失敗しました:net :: ERR_INSECURE_RESPONSE(インデックス):1キャッチされません(約束)DOMException:ServiceWorkerの登録に失敗しました:スクリプトにサポートされていないMIMEタイプ( 'text/html')があります。
Template.jsルートファイルのコード
export default ({ markup, css }) => {
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MERN Marketplace</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<style>
a{
text-decoration: none
}
</style>
<link rel="manifest" href="./manifest.json">
</head>
<body style="margin:0">
<div id="root">${markup}</div>
<style id="jss-server-side">${css}</style>
<script type="text/javascript" src="/dist/bundle.js"></script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function() {
console.log("Service Worker Registered");
});
}
</script>
</body>
</html>`;
};