|
<!DOCTYPE html> |
|
<html lang="ja"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<title>1ファイル完結 Service Worker + iframe + Blob</title> |
|
</head> |
|
<body> |
|
<h1>Service Worker Blob + iframe で1ファイル完結</h1> |
|
<div id="log">Service Worker 状態ログ</div> |
|
|
|
|
|
<script type="text/javascript" id="sw-script"> |
|
self.addEventListener('fetch', event => { |
|
const { request } = event; |
|
event.respondWith( |
|
fetch(request).then(response => { |
|
const resClone = response.clone(); |
|
resClone.text().then(body => { |
|
self.clients.matchAll().then(clients => { |
|
clients.forEach(client => { |
|
client.postMessage({ |
|
type: 'fetch-log', |
|
url: request.url, |
|
status: response.status, |
|
body: body.slice(0, 100) |
|
}); |
|
}); |
|
}); |
|
}); |
|
return response; |
|
}).catch(error => { |
|
self.clients.matchAll().then(clients => { |
|
clients.forEach(client => { |
|
client.postMessage({ |
|
type: 'fetch-error', |
|
url: request.url, |
|
error: error.toString() |
|
}); |
|
}); |
|
}); |
|
throw error; |
|
}) |
|
); |
|
}); |
|
</script> |
|
|
|
<script> |
|
(async () => { |
|
const log = document.getElementById('log'); |
|
|
|
// 2. sw-scriptタグからService Workerコードを取得 |
|
const swScript = document.getElementById('sw-script').textContent.trim(); |
|
|
|
// 3. iframe用のHTMLをBlob化する |
|
const iframeHtml = ` |
|
<!DOCTYPE html> |
|
<html lang="ja"> |
|
<head><meta charset="UTF-8"><title>SW iframe</title></head> |
|
<body> |
|
<script> |
|
// Service Worker スクリプトをevalで実行して登録 |
|
const swCode = \`${swScript.replace(/`/g, '\\`')}\`; |
|
|
|
// BlobにしてURLを作成 |
|
const blob = new Blob([swCode], {type: 'application/javascript'}); |
|
const swBlobUrl = URL.createObjectURL(blob); |
|
|
|
navigator.serviceWorker.register(swBlobUrl).then(reg => { |
|
parent.postMessage({type: 'sw-registered'}, '*'); |
|
}).catch(err => { |
|
parent.postMessage({type: 'sw-error', error: err.toString()}, '*'); |
|
}); |
|
|
|
// Service Worker からのメッセージを親に転送 |
|
navigator.serviceWorker.addEventListener('message', e => { |
|
parent.postMessage({type: 'sw-message', data: e.data}, '*'); |
|
}); |
|
<\/script> |
|
</body> |
|
</html>`; |
|
|
|
const blob = new Blob([iframeHtml], { type: 'text/html' }); |
|
const iframeUrl = URL.createObjectURL(blob); |
|
|
|
// 4. iframeを作成して読み込む |
|
const iframe = document.createElement('iframe'); |
|
iframe.src = iframeUrl; |
|
iframe.style.width = '100%'; |
|
iframe.style.height = '200px'; |
|
document.body.appendChild(iframe); |
|
|
|
// 5. iframeからのメッセージを受け取り、ログ表示 |
|
window.addEventListener('message', (event) => { |
|
if (!event.data) return; |
|
const { type, error, data } = event.data; |
|
|
|
if (type === 'sw-registered') { |
|
log.textContent = '✅ Service Worker 登録成功!通信を監視中...'; |
|
} else if (type === 'sw-error') { |
|
log.textContent = '❌ Service Worker 登録失敗: ' + error; |
|
} else if (type === 'sw-message') { |
|
const { url, status, body, error: fetchError } = data; |
|
const div = document.createElement('div'); |
|
if (data.type === 'fetch-log') { |
|
div.innerHTML = `✅ ${url} - ${status}<pre>${body}</pre>`; |
|
div.style.color = 'green'; |
|
} else if (data.type === 'fetch-error') { |
|
div.innerHTML = `❌ ${url} - エラー: ${fetchError}`; |
|
div.style.color = 'red'; |
|
} |
|
document.body.appendChild(div); |
|
} |
|
}); |
|
|
|
// 6. iframe内で動くfetchテスト(親で発火してもService Workerが拾う) |
|
fetch('https://jsonplaceholder.typicode.com/posts/1'); |
|
fetch('https://httpstat.us/404'); // エラーの例 |
|
|
|
})(); |
|
</script> |
|
</body> |
|
</html> |
|
|