<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SharedWorker 與 Worker 的區別</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 40px;
padding: 20px;
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
color: white;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
}
.comparison {
display: flex;
flex-wrap: wrap;
gap: 30px;
margin-bottom: 40px;
}
.card {
flex: 1;
min-width: 300px;
background: white;
border-radius: 10px;
padding: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.worker-card {
border-top: 5px solid #ff6b6b;
}
.shared-worker-card {
border-top: 5px solid #4cd97b;
}
.card h2 {
display: flex;
align-items: center;
margin-bottom: 20px;
font-size: 1.8rem;
}
.worker-card h2:before {
content: "🔒";
margin-right: 10px;
}
.shared-worker-card h2:before {
content: "🔗";
margin-right: 10px;
}
.feature-list {
list-style-type: none;
margin: 20px 0;
}
.feature-list li {
padding: 10px 0;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
}
.feature-list li:before {
content: "✓";
margin-right: 10px;
font-weight: bold;
}
.worker-card .feature-list li:before {
color: #ff6b6b;
}
.shared-worker-card .feature-list li:before {
color: #4cd97b;
}
.demo-section {
display: flex;
flex-wrap: wrap;
gap: 30px;
margin-bottom: 40px;
}
.demo-panel {
flex: 1;
min-width: 300px;
background: white;
border-radius: 10px;
padding: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.demo-panel h3 {
margin-bottom: 20px;
font-size: 1.5rem;
color: #444;
}
.demo-controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
button {
padding: 10px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
}
.worker-btn {
background-color: #ff6b6b;
color: white;
}
.worker-btn:hover {
background-color: #ff5252;
}
.shared-worker-btn {
background-color: #4cd97b;
color: white;
}
.shared-worker-btn:hover {
background-color: #38cc6c;
}
.output {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 5px;
padding: 15px;
min-height: 150px;
max-height: 300px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
white-space: pre-wrap;
}
.status {
margin-top: 10px;
padding: 10px;
border-radius: 5px;
font-weight: bold;
}
.status.connected {
background-color: #e8f5e9;
color: #2e7d32;
}
.status.disconnected {
background-color: #ffebee;
color: #c62828;
}
.explanation {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 5px;
padding: 15px;
margin: 20px 0;
}
.code-example {
background-color: #2d2d2d;
color: #f8f8f2;
border-radius: 5px;
padding: 20px;
margin-top: 30px;
overflow-x: auto;
}
.code-example h3 {
color: #f8f8f2;
margin-bottom: 15px;
}
pre {
white-space: pre-wrap;
line-height: 1.5;
}
.highlight {
color: #ff79c6;
}
.comment {
color: #6272a4;
}
.browser-support {
background: white;
border-radius: 10px;
padding: 25px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
margin-bottom: 40px;
}
.browser-support h2 {
margin-bottom: 20px;
text-align: center;
color: #444;
}
.support-table {
width: 100%;
border-collapse: collapse;
}
.support-table th, .support-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.support-table th {
background-color: #f8f9fa;
}
.supported {
color: #4cd97b;
font-weight: bold;
}
.not-supported {
color: #ff6b6b;
font-weight: bold;
}
footer {
text-align: center;
padding: 20px;
color: #666;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.comparison, .demo-section {
flex-direction: column;
}
.card, .demo-panel {
min-width: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>SharedWorker 與 Worker 的區別</h1>
<p class="subtitle">理解Web Workers中的兩種不同類型及其應用場景</p>
</header>
<div class="explanation">
<h3>⚠️ 重要説明:SharedWorker的限制</h3>
<p>由於瀏覽器安全限制,使用Blob URL創建的SharedWorker在不同標籤頁中無法真正共享。每個標籤頁會創建獨立的SharedWorker實例。</p>
<p>要體驗真正的SharedWorker共享功能,需要:</p>
<ol>
<li>通過HTTP服務器運行此頁面(不能直接打開HTML文件)</li>
<li>使用固定的JS文件路徑創建SharedWorker</li>
</ol>
<p>當前演示使用模擬方式展示SharedWorker的概念和工作原理。</p>
</div>
<div class="comparison">
<div class="card worker-card">
<h2>專用Worker (Dedicated Worker)</h2>
<p>專用Worker與創建它的腳本一對一關聯,只能被創建它的頁面訪問。</p>
<ul class="feature-list">
<li>與創建它的腳本一對一關聯</li>
<li>生命週期與創建頁面綁定</li>
<li>無法被其他頁面或Worker訪問</li>
<li>所有主流瀏覽器都支持</li>
<li>適用於單頁面內的複雜計算任務</li>
</ul>
</div>
<div class="card shared-worker-card">
<h2>共享Worker (SharedWorker)</h2>
<p>共享Worker可以被多個腳本共享,只要這些腳本與共享Worker同源。</p>
<ul class="feature-list">
<li>可以被多個瀏覽器上下文(窗口、iframe等)共享</li>
<li>生命週期獨立於創建它的頁面</li>
<li>通過端口(port)與不同頁面通信</li>
<li>瀏覽器支持相對較少</li>
<li>適用於多標籤頁應用的數據同步</li>
</ul>
</div>
</div>
<div class="demo-section">
<div class="demo-panel">
<h3>Worker 演示</h3>
<div class="demo-controls">
<button class="worker-btn" id="startWorker">啓動Worker</button>
<button class="worker-btn" id="sendToWorker">發送消息</button>
<button class="worker-btn" id="terminateWorker">終止Worker</button>
</div>
<div class="output" id="workerOutput">Worker輸出將顯示在這裏...</div>
<div id="workerStatus" class="status disconnected">Worker狀態: 未連接</div>
</div>
<div class="demo-panel">
<h3>SharedWorker 演示(模擬)</h3>
<div class="demo-controls">
<button class="shared-worker-btn" id="startSharedWorker">啓動SharedWorker</button>
<button class="shared-worker-btn" id="sendToSharedWorker">發送消息</button>
<button class="shared-worker-btn" id="terminateSharedWorker">斷開連接</button>
<button class="shared-worker-btn" id="newTab">在新標籤頁中打開</button>
</div>
<div class="output" id="sharedWorkerOutput">SharedWorker輸出將顯示在這裏...</div>
<div id="sharedWorkerStatus" class="status disconnected">SharedWorker狀態: 未連接</div>
<p style="margin-top: 10px; font-size: 0.9rem; color: #666;">
提示:由於瀏覽器限制,當前演示為模擬效果。真實環境中需要服務器支持。
</p>
</div>
</div>
<div class="code-example">
<h3>真實環境中的SharedWorker代碼示例</h3>
<pre><code>// <span class="comment">// 主頁面代碼 (main.html)</span>
<span class="comment">// 使用固定的JS文件路徑創建SharedWorker</span>
const sharedWorker = new SharedWorker('/js/shared-worker.js');
sharedWorker.port.start();
sharedWorker.port.onmessage = function(event) {
console.log('收到來自SharedWorker的消息:', event.data);
};
<span class="comment">// 發送消息到SharedWorker</span>
sharedWorker.port.postMessage('Hello from main page!');
<span class="comment">// SharedWorker代碼 (shared-worker.js)</span>
let connectionCount = 0;
let ports = [];
self.onconnect = function(event) {
<span class="comment">// 獲取連接的端口</span>
const port = event.ports[0];
connectionCount++;
<span class="comment">// 保存端口引用</span>
ports.push(port);
<span class="comment">// 向新連接的客户端發送歡迎消息</span>
port.postMessage(`歡迎!你是第${connectionCount}個連接的客户端`);
<span class="comment">// 向所有客户端廣播連接更新</span>
broadcastMessage(`系統通知:當前有${connectionCount}個客户端連接`);
<span class="comment">// 處理來自客户端的消息</span>
port.onmessage = function(event) {
const message = event.data;
broadcastMessage(`客户端消息: ${message}`);
};
<span class="comment">// 處理端口關閉</span>
port.onclose = function() {
const index = ports.indexOf(port);
if (index > -1) {
ports.splice(index, 1);
connectionCount--;
broadcastMessage(`系統通知:客户端斷開,剩餘${connectionCount}個連接`);
}
};
port.start();
};
function broadcastMessage(message) {
ports.forEach(port => {
port.postMessage(message);
});
}</code></pre>
</div>
<div class="browser-support">
<h2>瀏覽器支持情況</h2>
<table class="support-table">
<thead>
<tr>
<th>瀏覽器</th>
<th>Worker</th>
<th>SharedWorker</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chrome</td>
<td class="supported">✓ 支持</td>
<td class="supported">✓ 支持</td>
</tr>
<tr>
<td>Firefox</td>
<td class="supported">✓ 支持</td>
<td class="supported">✓ 支持</td>
</tr>
<tr>
<td>Safari</td>
<td class="supported">✓ 支持</td>
<td class="supported">✓ 支持 (14.1+)</td>
</tr>
<tr>
<td>Edge</td>
<td class="supported">✓ 支持</td>
<td class="supported">✓ 支持</td>
</tr>
<tr>
<td>Internet Explorer</td>
<td class="supported">✓ 支持 (10+)</td>
<td class="not-supported">✗ 不支持</td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>© 2023 Web Workers 演示 | 注意:真正的SharedWorker需要服務器環境才能正常工作</p>
</footer>
</div>
<script>
// Worker 演示代碼
let worker;
const workerOutput = document.getElementById('workerOutput');
const workerStatus = document.getElementById('workerStatus');
document.getElementById('startWorker').addEventListener('click', function() {
if (worker) {
workerOutput.textContent += '\nWorker已經存在,先終止舊Worker';
worker.terminate();
}
// 創建Worker的Blob URL
const workerScript = `
let messageCount = 0;
self.onmessage = function(e) {
messageCount++;
const message = e.data;
self.postMessage('Worker收到第' + messageCount + '條消息: ' + message + ' | 時間: ' + new Date().toLocaleTimeString());
// 模擬一些工作
let result = 0;
for (let i = 0; i < 10000000; i++) {
result += Math.sqrt(i);
}
self.postMessage('計算完成! 結果: ' + result.toString().substring(0, 10) + '...');
};
`;
const blob = new Blob([workerScript], { type: 'application/javascript' });
const blobUrl = URL.createObjectURL(blob);
worker = new Worker(blobUrl);
worker.onmessage = function(e) {
workerOutput.textContent += '\n' + e.data;
workerOutput.scrollTop = workerOutput.scrollHeight;
};
worker.onerror = function(e) {
workerOutput.textContent += '\nWorker錯誤: ' + e.message;
};
workerOutput.textContent = 'Worker已啓動!';
workerStatus.textContent = 'Worker狀態: 已連接';
workerStatus.className = 'status connected';
});
document.getElementById('sendToWorker').addEventListener('click', function() {
if (worker) {
worker.postMessage('消息 #' + Math.floor(Math.random() * 100));
} else {
workerOutput.textContent += '\n請先啓動Worker!';
}
});
document.getElementById('terminateWorker').addEventListener('click', function() {
if (worker) {
worker.terminate();
worker = null;
workerOutput.textContent += '\nWorker已終止!';
workerStatus.textContent = 'Worker狀態: 未連接';
workerStatus.className = 'status disconnected';
}
});
// SharedWorker 模擬演示
// 由於瀏覽器限制,我們使用localStorage模擬SharedWorker的共享效果
let sharedWorker;
const sharedWorkerOutput = document.getElementById('sharedWorkerOutput');
const sharedWorkerStatus = document.getElementById('sharedWorkerStatus');
// 生成唯一的頁面ID
const pageId = 'page_' + Math.random().toString(36).substr(2, 9);
let connectionCount = 1; // 模擬連接計數
// 模擬SharedWorker的消息處理
function simulateSharedWorker() {
// 存儲當前頁面的連接狀態
localStorage.setItem('shared_worker_page_' + pageId, 'connected');
// 計算"已連接"的頁面數量
let connectedPages = 0;
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key.startsWith('shared_worker_page_') && localStorage.getItem(key) === 'connected') {
connectedPages++;
}
}
// 模擬SharedWorker的響應
return {
connectionCount: connectedPages,
pageId: pageId
};
}
document.getElementById('startSharedWorker').addEventListener('click', function() {
if (sharedWorker) {
sharedWorkerOutput.textContent += '\nSharedWorker已經連接';
return;
}
try {
// 模擬SharedWorker連接
const result = simulateSharedWorker();
connectionCount = result.connectionCount;
sharedWorker = {
port: {
postMessage: function(message) {
// 模擬消息廣播到所有"已連接"的頁面
const timestamp = new Date().toLocaleTimeString();
const response = `SharedWorker收到消息: ${message} | 來自: ${pageId} | 時間: ${timestamp}`;
// 存儲消息以便其他頁面可以讀取(模擬廣播)
localStorage.setItem('shared_worker_broadcast', response);
localStorage.setItem('shared_worker_broadcast_time', Date.now().toString());
// 也顯示在當前頁面
setTimeout(() => {
sharedWorkerOutput.textContent += '\n' + response;
sharedWorkerOutput.scrollTop = sharedWorkerOutput.scrollHeight;
}, 100);
},
close: function() {
localStorage.setItem('shared_worker_page_' + pageId, 'disconnected');
sharedWorker = null;
}
}
};
// 模擬SharedWorker的歡迎消息
sharedWorkerOutput.textContent = `SharedWorker已連接! (頁面ID: ${pageId})`;
sharedWorkerOutput.textContent += `\n歡迎! 當前有 ${connectionCount} 個客户端連接到SharedWorker`;
sharedWorkerStatus.textContent = 'SharedWorker狀態: 已連接';
sharedWorkerStatus.className = 'status connected';
// 監聽其他頁面的廣播消息
startBroadcastListener();
} catch (e) {
sharedWorkerOutput.textContent = 'SharedWorker啓動失敗: ' + e.message;
}
});
document.getElementById('sendToSharedWorker').addEventListener('click', function() {
if (sharedWorker) {
sharedWorker.port.postMessage('消息 #' + Math.floor(Math.random() * 100));
} else {
sharedWorkerOutput.textContent += '\n請先啓動SharedWorker!';
}
});
document.getElementById('terminateSharedWorker').addEventListener('click', function() {
if (sharedWorker) {
sharedWorker.port.close();
sharedWorker = null;
sharedWorkerOutput.textContent += '\nSharedWorker連接已關閉!';
sharedWorkerStatus.textContent = 'SharedWorker狀態: 未連接';
sharedWorkerStatus.className = 'status disconnected';
// 停止監聽廣播
stopBroadcastListener();
}
});
document.getElementById('newTab').addEventListener('click', function() {
window.open(window.location.href, '_blank');
});
// 廣播監聽器
let broadcastListener;
function startBroadcastListener() {
let lastBroadcastTime = localStorage.getItem('shared_worker_broadcast_time') || '0';
broadcastListener = setInterval(() => {
const currentBroadcastTime = localStorage.getItem('shared_worker_broadcast_time');
if (currentBroadcastTime && currentBroadcastTime !== lastBroadcastTime) {
lastBroadcastTime = currentBroadcastTime;
const message = localStorage.getItem('shared_worker_broadcast');
if (message && !message.includes(pageId)) { // 不顯示自己發送的消息
sharedWorkerOutput.textContent += '\n' + message;
sharedWorkerOutput.scrollTop = sharedWorkerOutput.scrollHeight;
}
}
}, 500);
}
function stopBroadcastListener() {
if (broadcastListener) {
clearInterval(broadcastListener);
broadcastListener = null;
}
}
// 頁面卸載時清理資源
window.addEventListener('beforeunload', function() {
if (worker) {
worker.terminate();
}
if (sharedWorker) {
localStorage.setItem('shared_worker_page_' + pageId, 'disconnected');
stopBroadcastListener();
}
});
</script>
</body>
</html>