<!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>