乐闻世界logo
搜索文章和话题

面试题手册

Gin 框架中如何实现 WebSocket 支持?

Gin 框架中的 WebSocket 支持和实现方法如下:1. WebSocket 基础Gin 本身不直接支持 WebSocket,但可以通过集成第三方库来实现 WebSocket 功能。常用的库包括:gorilla/websocket: 最流行的 Go WebSocket 库gobwas/ws: 高性能的 WebSocket 库2. 使用 gorilla/websocket 实现2.1 安装依赖go get github.com/gorilla/websocket2.2 创建 WebSocket 升级器var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { // 允许所有来源,生产环境应该限制 return true },}2.3 WebSocket 处理函数func handleWebSocket(c *gin.Context) { // 升级 HTTP 连接到 WebSocket conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { log.Printf("WebSocket upgrade error: %v", err) return } defer conn.Close() // 处理 WebSocket 连接 for { // 读取消息 messageType, message, err := conn.ReadMessage() if err != nil { log.Printf("Read error: %v", err) break } log.Printf("Received: %s", message) // 发送响应 err = conn.WriteMessage(messageType, []byte("Echo: "+string(message))) if err != nil { log.Printf("Write error: %v", err) break } }}2.4 注册 WebSocket 路由func main() { r := gin.Default() r.GET("/ws", handleWebSocket) r.Run(":8080")}3. WebSocket 管理器3.1 创建连接管理器type Client struct { Conn *websocket.Conn Send chan []byte}type Hub struct { clients map[*Client]bool broadcast chan []byte register chan *Client unregister chan *Client}var hub = Hub{ clients: make(map[*Client]bool), broadcast: make(chan []byte), register: make(chan *Client), unregister: make(chan *Client),}func (h *Hub) Run() { for { select { case client := <-h.register: h.clients[client] = true case client := <-h.unregister: if _, ok := h.clients[client]; ok { delete(h.clients, client) close(client.Send) } case message := <-h.broadcast: for client := range h.clients { select { case client.Send <- message: default: close(client.Send) delete(h.clients, client) } } } }}3.2 客户端处理func (c *Client) readPump() { defer func() { hub.unregister <- c c.Conn.Close() }() for { _, message, err := c.Conn.ReadMessage() if err != nil { break } hub.broadcast <- message }}func (c *Client) writePump() { defer c.Conn.Close() for { select { case message, ok := <-c.Send: if !ok { return } err := c.Conn.WriteMessage(websocket.TextMessage, message) if err != nil { return } } }}4. 实时聊天示例4.1 聊天室处理函数func handleChat(c *gin.Context) { conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { log.Printf("WebSocket upgrade error: %v", err) return } client := &Client{ Conn: conn, Send: make(chan []byte, 256), } hub.register <- client go client.writePump() go client.readPump()}4.2 广播消息func broadcastMessage(message string) { hub.broadcast <- []byte(message)}5. WebSocket 认证5.1 Token 认证func authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { token := c.Query("token") if token == "" { c.JSON(401, gin.H{"error": "Unauthorized"}) c.Abort() return } // 验证 token if !validateToken(token) { c.JSON(401, gin.H{"error": "Invalid token"}) c.Abort() return } c.Next() }}// 使用认证中间件r.GET("/ws", authMiddleware(), handleWebSocket)6. 心跳检测6.1 实现 Ping/Pongfunc handleWebSocketWithPing(c *gin.Context) { conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { return } defer conn.Close() // 设置 Pong 处理器 conn.SetPongHandler(func(string) error { log.Println("Received pong") return nil }) ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() done := make(chan struct{}) go func() { defer close(done) for { _, _, err := conn.ReadMessage() if err != nil { return } } }() for { select { case <-ticker.C: // 发送 Ping err := conn.WriteMessage(websocket.PingMessage, []byte{}) if err != nil { return } case <-done: return } }}7. 最佳实践连接管理使用连接池管理多个 WebSocket 连接实现心跳检测防止连接断开提供优雅的连接关闭机制消息处理使用消息队列处理高并发消息实现消息确认机制处理消息序列化和反序列化安全性实现 WebSocket 认证和授权使用 WSS(WebSocket Secure)加密连接限制连接频率和消息大小性能优化使用缓冲通道减少阻塞实现消息压缩合理设置读写缓冲区大小错误处理记录连接错误和消息错误实现自动重连机制提供错误恢复策略通过以上方法,可以在 Gin 框架中实现功能完善的 WebSocket 应用。
阅读 0·2月21日 15:12

Web Worker 的安全性有哪些考虑?

Web Worker 的安全性是开发者需要重点关注的问题,特别是在处理敏感数据或跨域请求时。主要安全考虑1. 同源策略(Same-Origin Policy)Web Worker 遵循同源策略,只能加载与主页面同源的脚本。// ❌ 跨域加载 Worker - 会失败const worker = new Worker('https://malicious-site.com/worker.js');// ✅ 同源加载 Worker - 正常工作const worker = new Worker('/workers/worker.js');// ✅ 使用 Blob URL 绕过(但内容仍需同源)const workerCode = fetch('/trusted-source/worker.js') .then(response => response.text()) .then(code => { const blob = new Blob([code], { type: 'application/javascript' }); return new Worker(URL.createObjectURL(blob)); });2. 数据隔离Worker 运行在独立的线程中,无法直接访问主线程的变量和 DOM。// 主线程const secretData = 'sensitive information';const worker = new Worker('worker.js');// Worker 无法直接访问 secretData// worker.jsconsole.log(secretData); // ReferenceError: secretData is not defined// ✅ 通过消息传递数据(注意数据会被深拷贝)worker.postMessage({ data: secretData });3. 消息传递安全使用 postMessage 传递数据时,数据会被结构化克隆。// ❌ 不安全的消息传递worker.postMessage({ password: userPassword, token: authToken });// ✅ 安全的消息传递 - 只传递必要数据worker.postMessage({ taskId: generateId(), encryptedData: encryptData(data)});// ✅ 使用 Transferable Objects 避免数据拷贝const buffer = new ArrayBuffer(1024);worker.postMessage({ buffer }, [buffer]);// buffer 现在为空,数据已转移4. 防止 XSS 攻击避免在 Worker 中执行不可信的代码。// ❌ 危险:执行用户输入的代码const userCode = getUserInput();const blob = new Blob([userCode], { type: 'application/javascript' });const worker = new Worker(URL.createObjectURL(blob));// ✅ 安全:使用预定义的 Worker 脚本const worker = new Worker('/trusted-worker.js');worker.postMessage({ userInput: getUserInput() });5. CORS 配置如果需要跨域加载 Worker 资源,需要正确配置 CORS。// 服务器端需要设置 CORS 头部// Access-Control-Allow-Origin: https://your-domain.com// Access-Control-Allow-Methods: GET, POST// Access-Control-Allow-Headers: Content-Type// 客户端const worker = new Worker('https://api.example.com/worker.js');安全最佳实践1. 输入验证// worker.jsself.onmessage = function(e) { const data = e.data; // 验证输入数据 if (!isValidInput(data)) { console.error('Invalid input detected'); return; } // 处理数据 const result = processData(data); self.postMessage(result);};function isValidInput(data) { // 检查数据类型 if (typeof data !== 'object' || data === null) { return false; } // 检查必需字段 if (!data.type || !data.payload) { return false; } // 检查数据大小 if (JSON.stringify(data).length > MAX_SIZE) { return false; } return true;}2. 错误处理// 主线程const worker = new Worker('worker.js');worker.onerror = function(event) { console.error('Worker error:', event.message); // 记录错误信息(不暴露敏感信息) logError({ message: sanitizeErrorMessage(event.message), filename: event.filename, lineno: event.lineno }); // 根据错误类型采取适当措施 if (isSecurityError(event.error)) { terminateWorker(); }};function sanitizeErrorMessage(message) { // 移除可能包含敏感信息的部分 return message.replace(/password|token|secret/gi, '[REDACTED]');}3. 资源限制// worker.jsconst MAX_EXECUTION_TIME = 5000; // 5秒let startTime = null;self.onmessage = function(e) { startTime = performance.now(); try { const result = processWithTimeout(e.data, MAX_EXECUTION_TIME); self.postMessage(result); } catch (error) { if (error instanceof TimeoutError) { self.postMessage({ error: 'Operation timed out' }); } else { self.postMessage({ error: 'Processing failed' }); } }};function processWithTimeout(data, timeout) { const result = []; for (let i = 0; i < data.length; i++) { // 检查是否超时 if (performance.now() - startTime > timeout) { throw new TimeoutError(); } result.push(processItem(data[i])); } return result;}4. 消息加密// 使用 Web Crypto API 加密敏感数据async function encryptData(data, key) { const encoder = new TextEncoder(); const encodedData = encoder.encode(data); const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, encodedData ); return { encrypted, iv };}// 主线程const key = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);const encrypted = await encryptData(sensitiveData, key);worker.postMessage({ encrypted, iv });5. Worker 池管理class SecureWorkerPool { constructor(workerPath, poolSize = 4) { this.workerPath = workerPath; this.poolSize = poolSize; this.workers = []; this.taskQueue = []; this.init(); } init() { for (let i = 0; i < this.poolSize; i++) { const worker = new Worker(this.workerPath); // 添加错误处理 worker.onerror = (event) => { this.handleWorkerError(event, worker); }; worker.onmessage = (e) => this.handleMessage(e, worker); this.workers.push({ worker, busy: false, id: i }); } } handleWorkerError(event, workerObj) { console.error(`Worker ${workerObj.id} error:`, event.message); // 重启 Worker this.restartWorker(workerObj); } restartWorker(workerObj) { const oldWorker = workerObj.worker; oldWorker.terminate(); const newWorker = new Worker(this.workerPath); newWorker.onerror = (event) => this.handleWorkerError(event, workerObj); newWorker.onmessage = (e) => this.handleMessage(e, workerObj); workerObj.worker = newWorker; workerObj.busy = false; } execute(data) { return new Promise((resolve, reject) => { // 验证输入 if (!this.validateInput(data)) { reject(new Error('Invalid input')); return; } const availableWorker = this.workers.find(w => !w.busy); if (availableWorker) { availableWorker.busy = true; availableWorker.worker.postMessage({ data, resolve, reject }); } else { this.taskQueue.push({ data, resolve, reject }); } }); } validateInput(data) { // 实现输入验证逻辑 return true; }}Content Security Policy (CSP)配置 CSP 来限制 Worker 的行为:<!-- 在 HTML 头部添加 CSP --><meta http-equiv="Content-Security-Policy" content="worker-src 'self' https://trusted-domain.com">// 检查 Worker 是否受 CSP 限制if (self.isSecureContext) { console.log('Worker is running in secure context');}安全检查清单[ ] 验证所有输入数据[ ] 使用 HTTPS 加载 Worker 脚本[ ] 遵循同源策略[ ] 避免在 Worker 中执行不可信代码[ ] 实施错误处理和日志记录[ ] 限制 Worker 执行时间[ ] 加密敏感数据[ ] 配置适当的 CSP 策略[ ] 定期更新 Worker 代码[ ] 监控 Worker 行为和性能常见安全漏洞1. 数据泄露// ❌ 泄露敏感信息worker.postMessage({ password: userPassword });// ✅ 使用加密const encrypted = await encryptPassword(userPassword);worker.postMessage({ encrypted });2. 拒绝服务攻击// ❌ 无限制的处理worker.postMessage(hugeData);// ✅ 限制数据大小if (data.length > MAX_SIZE) { throw new Error('Data too large');}3. 代码注入// ❌ 执行用户代码eval(userCode);// ✅ 使用沙箱环境const sandbox = createSandbox();sandbox.execute(userCode);最佳实践总结输入验证:严格验证所有输入数据错误处理:完善的错误处理机制资源限制:限制执行时间和数据大小数据加密:敏感数据使用加密传输HTTPS:使用 HTTPS 加载 Worker 脚本CSP 配置:配置适当的内容安全策略定期审计:定期审查 Worker 代码的安全性监控日志:监控 Worker 行为并记录日志
阅读 0·2月21日 15:11

如何优化 Web Worker 的性能?

Web Worker 的性能优化是确保应用高效运行的关键。以下是多个方面的优化策略。1. Worker 创建和销毁优化复用 Worker 实例// ❌ 频繁创建和销毁(性能差)function processTask(data) { const worker = new Worker('worker.js'); worker.postMessage(data); worker.onmessage = function(e) { console.log(e.data); worker.terminate(); };}// ✅ 复用 Worker(性能好)const worker = new Worker('worker.js');const pendingTasks = [];function processTask(data) { return new Promise((resolve) => { const taskId = Date.now(); pendingTasks[taskId] = resolve; worker.postMessage({ taskId, data }); });}worker.onmessage = function(e) { const { taskId, result } = e.data; if (pendingTasks[taskId]) { pendingTasks[taskId](result); delete pendingTasks[taskId]; }};Worker 池模式class WorkerPool { constructor(workerPath, poolSize = 4) { this.workerPath = workerPath; this.poolSize = poolSize; this.workers = []; this.taskQueue = []; this.init(); } init() { for (let i = 0; i < this.poolSize; i++) { const worker = new Worker(this.workerPath); worker.onmessage = (e) => this.handleMessage(e, worker); this.workers.push({ worker, busy: false }); } } execute(data) { return new Promise((resolve) => { const availableWorker = this.workers.find(w => !w.busy); if (availableWorker) { availableWorker.busy = true; availableWorker.worker.postMessage({ data, resolve }); } else { this.taskQueue.push({ data, resolve }); } }); } handleMessage(event, workerObj) { const { result } = event.data; const pendingTask = workerObj.worker.pendingTask; if (pendingTask) { pendingTask.resolve(result); workerObj.worker.pendingTask = null; } workerObj.busy = false; if (this.taskQueue.length > 0) { const nextTask = this.taskQueue.shift(); workerObj.busy = true; workerObj.worker.pendingTask = nextTask; workerObj.worker.postMessage({ data: nextTask.data }); } }}// 使用 Worker 池const pool = new WorkerPool('worker.js', 4);pool.execute(largeData).then(result => console.log(result));2. 消息传递优化使用 Transferable Objects// ❌ 深拷贝(性能差)const buffer = new ArrayBuffer(1024 * 1024);worker.postMessage({ buffer }); // 拷贝 1MB 数据// ✅ 转移所有权(性能好)const buffer = new ArrayBuffer(1024 * 1024);worker.postMessage({ buffer }, [buffer]); // 零拷贝// buffer 现在为空,所有权已转移批量处理消息// ❌ 频繁发送小消息for (let i = 0; i < 10000; i++) { worker.postMessage({ index: i, value: data[i] });}// ✅ 批量发送worker.postMessage({ data: data.slice(0, 10000) });使用 SharedArrayBuffer(需要特定头部)// 需要服务器设置 COOP/COEP 头部// Cross-Origin-Opener-Policy: same-origin// Cross-Origin-Embedder-Policy: require-corpconst sharedBuffer = new SharedArrayBuffer(1024);const sharedArray = new Int32Array(sharedBuffer);worker.postMessage({ sharedBuffer }, [sharedBuffer]);// 主线程和 Worker 可以同时访问 sharedArraysharedArray[0] = 42;3. 数据处理优化分块处理大数据// worker.jsself.onmessage = function(e) { const { data, chunkSize } = e.data; const results = []; for (let i = 0; i < data.length; i += chunkSize) { const chunk = data.slice(i, i + chunkSize); const result = processChunk(chunk); results.push(result); // 定期报告进度 if (i % (chunkSize * 10) === 0) { self.postMessage({ type: 'progress', progress: i / data.length }); } } self.postMessage({ type: 'complete', results });};使用 WebAssembly 加速计算// worker.jsconst wasmModule = await WebAssembly.instantiateStreaming( fetch('compute.wasm'));self.onmessage = function(e) { const { data } = e.data; const result = wasmModule.instance.exports.compute(data); self.postMessage(result);};4. 内存管理优化及时释放资源// 主线程const worker = new Worker('worker.js');// 使用完毕后终止 Workerworker.terminate();// Worker 内部self.onmessage = function(e) { const result = process(e.data); self.postMessage(result); // 清理大对象 e.data = null;};避免内存泄漏// ❌ 可能导致内存泄漏const worker = new Worker('worker.js');worker.onmessage = function(e) { // 闭包引用大对象 const largeData = e.data; setTimeout(() => { console.log(largeData); }, 10000);};// ✅ 及时释放引用const worker = new Worker('worker.js');worker.onmessage = function(e) { const result = process(e.data); console.log(result); e.data = null; // 释放引用};5. 错误处理和监控错误处理const worker = new Worker('worker.js');worker.onerror = function(event) { console.error('Worker error:', event.message); console.error('Line:', event.lineno); console.error('File:', event.filename); // 根据错误类型决定是否重启 Worker if (isRecoverable(event.error)) { restartWorker(); }};worker.onmessageerror = function(event) { console.error('Message error:', event.data);};性能监控class WorkerMonitor { constructor(worker) { this.worker = worker; this.messageCount = 0; this.totalTime = 0; this.startTime = null; this.setupMonitoring(); } setupMonitoring() { this.worker.onmessage = (e) => { if (this.startTime) { const duration = performance.now() - this.startTime; this.totalTime += duration; this.messageCount++; console.log(`Message ${this.messageCount}: ${duration.toFixed(2)}ms`); console.log(`Average: ${(this.totalTime / this.messageCount).toFixed(2)}ms`); } }; } sendMessage(data) { this.startTime = performance.now(); this.worker.postMessage(data); } getStats() { return { messageCount: this.messageCount, totalTime: this.totalTime, averageTime: this.messageCount > 0 ? this.totalTime / this.messageCount : 0 }; }}// 使用监控const monitor = new WorkerMonitor(worker);monitor.sendMessage(data);6. 调试技巧使用 console.log// worker.jsself.onmessage = function(e) { console.log('[Worker] Received:', e.data); const result = process(e.data); console.log('[Worker] Result:', result); self.postMessage(result);};使用 Chrome DevTools打开 Chrome DevTools切换到 "Sources" 面板在左侧找到 Worker 脚本设置断点进行调试使用 postMessage 调试// 主线程worker.postMessage({ type: 'debug', data: { key: 'value' } });// Workerself.onmessage = function(e) { if (e.data.type === 'debug') { console.log('[Worker Debug]', e.data.data); }};最佳实践总结复用 Worker:避免频繁创建和销毁使用 Worker 池:管理多个 Worker 实例Transferable Objects:大数据使用转移而非拷贝批量处理:减少消息传递次数分块处理:大数据分块处理,定期报告进度及时释放资源:使用完毕后终止 Worker错误处理:添加完善的错误处理机制性能监控:监控 Worker 性能指标WebAssembly:计算密集型任务使用 WASMSharedArrayBuffer:需要共享内存时使用(注意安全限制)
阅读 0·2月21日 15:11

Web Worker 有哪些类型,它们之间有什么区别?

Web Worker 有多种类型,每种类型适用于不同的使用场景。1. Dedicated Worker(专用 Worker)特点专属于创建它的页面或脚本一对一关系:一个 Dedicated Worker 只能被一个脚本使用最常用的 Worker 类型创建方式// 从外部文件创建const worker = new Worker('worker.js');// 从 Blob URL 创建(内联 Worker)const workerCode = ` self.onmessage = function(e) { self.postMessage(e.data * 2); };`;const blob = new Blob([workerCode], { type: 'application/javascript' });const worker = new Worker(URL.createObjectURL(blob));使用场景单页面的后台计算任务图像处理数据加密/解密复杂的数学计算2. Shared Worker(共享 Worker)特点可以被多个页面或脚本共享多对多关系:多个脚本可以连接到同一个 Shared Worker适用于跨标签页的通信和状态共享创建方式// 创建 Shared Workerconst sharedWorker = new SharedWorker('shared-worker.js');// 连接到 WorkersharedWorker.port.start();// 发送消息sharedWorker.port.postMessage('Hello');// 接收消息sharedWorker.port.onmessage = function(event) { console.log('Received:', event.data);};Shared Worker 内部实现// shared-worker.jsconst connections = [];self.onconnect = function(event) { const port = event.ports[0]; connections.push(port); port.onmessage = function(e) { // 广播消息给所有连接 connections.forEach(conn => { conn.postMessage(e.data); }); }; port.start();};使用场景多标签页状态同步跨窗口通信共享数据缓存协同编辑应用3. Service Worker(服务 Worker)特点作为网络代理,拦截和处理网络请求独立于页面生命周期运行用于实现离线功能和推送通知必须在 HTTPS 环境下运行(localhost 除外)注册方式// 注册 Service Workerif ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { console.log('Service Worker registered:', registration); }) .catch(function(error) { console.log('Registration failed:', error); });}Service Worker 实现// service-worker.jsconst CACHE_NAME = 'my-cache-v1';const urlsToCache = [ '/', '/styles/main.css', '/script/main.js'];// 安装事件self.addEventListener('install', function(event) { event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) );});// 拦截请求self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; } return fetch(event.request); }) );});使用场景PWA(渐进式 Web 应用)离线缓存后台同步推送通知网络请求优化4. Worker 类型的对比| 特性 | Dedicated Worker | Shared Worker | Service Worker ||------|------------------|---------------|----------------|| 作用域 | 单页面 | 多页面共享 | 全局代理 || 生命周期 | 随页面销毁 | 所有连接断开后销毁 | 独立运行 || DOM 访问 | ❌ | ❌ | ❌ || 网络请求 | ❌ | ❌ | ✅ || HTTPS 要求 | ❌ | ❌ | ✅ || 通信方式 | postMessage | port.postMessage | postMessage + fetch |5. 其他 Worker 类型AudioWorklet用于音频处理在音频渲染线程中运行提供更精确的音频处理能力const audioContext = new AudioContext();audioContext.audioWorklet.addModule('audio-processor.js') .then(() => { const workletNode = new AudioWorkletNode(audioContext, 'audio-processor'); });OffscreenCanvas在 Worker 中进行 Canvas 渲染提升复杂图形渲染性能const worker = new Worker('canvas-worker.js');const offscreen = canvas.transferControlToOffscreen();worker.postMessage({ canvas: offscreen }, [offscreen]);选择建议Dedicated Worker:大多数后台计算任务Shared Worker:需要跨标签页共享状态Service Worker:需要离线功能或网络请求控制AudioWorklet:音频处理相关OffscreenCanvas:复杂 Canvas 渲染
阅读 0·2月21日 15:10

如何调试 Web Worker?

Web Worker 的调试比主线程调试更具挑战性,但有多种工具和技巧可以帮助开发者高效调试 Worker 代码。Chrome DevTools 调试1. 查看 Worker 线程打开 Chrome DevTools(F12)切换到 "Sources" 面板在左侧找到 "Threads" 或 "Workers" 部分选择对应的 Worker 线程进行调试2. 在 Worker 中设置断点// worker.jsself.onmessage = function(e) { const data = e.data; // 在这里设置断点 const result = processData(data); self.postMessage(result);};function processData(data) { // 可以在这里设置断点查看变量 let sum = 0; for (let i = 0; i < data.length; i++) { sum += data[i]; } return sum;}3. 使用 console.log// worker.jsself.onmessage = function(e) { console.log('[Worker] Received message:', e.data); const result = heavyComputation(e.data); console.log('[Worker] Computation result:', result); self.postMessage(result);};Firefox 开发者工具1. 调试 Worker打开 Firefox 开发者工具(F12)切换到 "Debugger" 面板在左侧找到 "Workers" 部分展开并选择要调试的 Worker2. Worker 控制台// worker.js// 在 Firefox 中,Worker 的 console.log 会显示在主控制台console.log('Worker message');// 也可以使用 Worker 特定的调试信息self.postMessage({ type: 'debug', message: 'Debug info' });调试技巧1. 消息追踪// 主线程const worker = new Worker('worker.js');// 添加消息发送追踪const originalPostMessage = worker.postMessage.bind(worker);worker.postMessage = function(data) { console.log('[Main → Worker]', data); return originalPostMessage(data);};// 添加消息接收追踪worker.addEventListener('message', function(e) { console.log('[Worker → Main]', e.data);});2. 错误捕获// 主线程worker.onerror = function(event) { console.error('Worker Error:'); console.error('Message:', event.message); console.error('Filename:', event.filename); console.error('Line:', event.lineno); console.error('Column:', event.colno); console.error('Error Object:', event.error);};// Worker 内部self.onerror = function(event) { console.error('Worker Internal Error:', event.message); // 可以选择不阻止默认行为 // event.preventDefault();};// 使用 try-catchself.onmessage = function(e) { try { const result = riskyOperation(e.data); self.postMessage(result); } catch (error) { console.error('Error in message handler:', error); self.postMessage({ error: error.message, stack: error.stack }); }};3. 性能分析// worker.jsself.onmessage = function(e) { const startTime = performance.now(); const result = heavyComputation(e.data); const endTime = performance.now(); const duration = endTime - startTime; console.log(`[Worker] Computation took ${duration.toFixed(2)}ms`); self.postMessage({ result, duration });};4. 状态监控// worker.jslet state = { messageCount: 0, totalProcessingTime: 0, lastMessageTime: null};self.onmessage = function(e) { const startTime = performance.now(); state.messageCount++; state.lastMessageTime = new Date().toISOString(); const result = processMessage(e.data); const endTime = performance.now(); state.totalProcessingTime += (endTime - startTime); self.postMessage({ result, state });};// 定期报告状态setInterval(() => { console.log('[Worker] State:', state);}, 5000);高级调试技术1. 使用 Source Map// 在 Worker 脚本中添加 source map// worker.js// # sourceMappingURL=worker.js.map// 或者在创建 Worker 时指定const worker = new Worker('worker.js');worker.addEventListener('message', function(e) { console.log(e.data);});2. 条件断点在 Chrome DevTools 中设置条件断点:// 在 processData 函数中设置条件断点// 条件:data.length > 1000function processData(data) { let sum = 0; for (let i = 0; i < data.length; i++) { sum += data[i]; } return sum;}3. 日志级别// worker.jsconst LogLevel = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3};let currentLogLevel = LogLevel.INFO;function log(level, message, data) { if (level >= currentLogLevel) { const prefix = { [LogLevel.DEBUG]: '[DEBUG]', [LogLevel.INFO]: '[INFO]', [LogLevel.WARN]: '[WARN]', [LogLevel.ERROR]: '[ERROR]' }[level]; console.log(`${prefix} ${message}`, data || ''); }}self.onmessage = function(e) { log(LogLevel.DEBUG, 'Received message', e.data); try { const result = processData(e.data); log(LogLevel.INFO, 'Processing complete', { result }); self.postMessage(result); } catch (error) { log(LogLevel.ERROR, 'Processing failed', { error: error.message }); }};4. 消息序列化检查// 检查消息是否可以正确序列化function checkSerializable(data) { try { const cloned = structuredClone(data); console.log('[Worker] Data is serializable'); return true; } catch (error) { console.error('[Worker] Data is not serializable:', error); return false; }}self.onmessage = function(e) { if (!checkSerializable(e.data)) { self.postMessage({ error: 'Data is not serializable' }); return; } // 处理消息};调试工具和库1. Worker DevTools Extension使用 Chrome 扩展 "Worker DevTools" 可以:查看 Worker 的 console 输出检查 Worker 的网络请求监控 Worker 的性能2. 使用调试代理// debug-worker.jsconst originalWorker = self.Worker;self.Worker = function(scriptURL) { console.log('[Debug] Creating Worker:', scriptURL); const worker = new originalWorker(scriptURL); const originalPostMessage = worker.postMessage.bind(worker); worker.postMessage = function(data) { console.log('[Debug] Main → Worker:', data); return originalPostMessage(data); }; worker.addEventListener('message', function(e) { console.log('[Debug] Worker → Main:', e.data); }); return worker;};常见问题排查1. Worker 无法启动// 检查 Worker 文件路径const worker = new Worker('/workers/worker.js'); // 确保路径正确// 检查浏览器支持if (typeof Worker === 'undefined') { console.error('Web Workers are not supported in this browser');}// 检查同源策略// 确保 Worker 脚本与主页面同源2. 消息未到达// 检查是否启动了端口(SharedWorker)const sharedWorker = new SharedWorker('worker.js');sharedWorker.port.start(); // 必须调用 start()// 检查消息格式// 确保消息可以被结构化克隆3. 内存泄漏// 使用 Chrome DevTools 的 Memory 面板// 1. 拍摄堆快照// 2. 执行操作// 3. 再次拍摄堆快照// 4. 比较两个快照,查找内存增长// Worker 中释放引用self.onmessage = function(e) { const result = process(e.data); self.postMessage(result); e.data = null; // 释放引用};最佳实践使用有意义的日志:添加清晰的日志信息错误处理:在 Worker 和主线程都添加错误处理性能监控:监控 Worker 的执行时间和资源使用消息追踪:追踪消息的发送和接收使用调试工具:充分利用浏览器开发者工具条件断点:使用条件断点减少调试时间日志级别:使用不同的日志级别过滤信息定期检查:定期检查 Worker 的状态和性能
阅读 0·2月21日 15:10

Web Worker 有哪些限制,如何解决这些限制?

Web Worker 有一些重要的限制,了解这些限制对于正确使用 Worker 至关重要。主要限制1. 无法访问 DOMWeb Worker 无法直接访问以下对象:windowdocumentDOM 元素(如 document.getElementById)parent(父窗口)// ❌ 在 Worker 中无法执行document.getElementById('myElement'); // 报错window.innerWidth; // 报错// ✅ 正确做法:通过消息传递// 主线程worker.postMessage({ action: 'getElement', id: 'myElement' });// Workerself.onmessage = function(e) { // 处理数据,返回结果 self.postMessage({ result: processedData });};2. 无法使用某些 APIWorker 中不可用的 API:localStorage、sessionStorageIndexedDB(但可以通过 IndexedDB 的异步 API 使用)document.cookiealert()、confirm()、prompt()history APInavigator 的部分属性// ❌ Worker 中不可用localStorage.setItem('key', 'value'); // 报错alert('Hello'); // 报错// ✅ 可用fetch('/api/data'); // ✅WebSocket; // ✅XMLHttpRequest; // ✅setTimeout/setInterval; // ✅3. 同源策略限制Worker 脚本必须与主页面同源,否则会抛出安全错误。// ❌ 跨域加载 Workerconst worker = new Worker('https://other-domain.com/worker.js'); // 报错// ✅ 同源加载const worker = new Worker('/workers/worker.js'); // 正常// ✅ 使用 Blob URL 绕过(但内容仍需同源)const workerCode = 'self.onmessage = ...';const blob = new Blob([workerCode], { type: 'application/javascript' });const worker = new Worker(URL.createObjectURL(blob));4. 无法同步加载脚本Worker 内部只能使用 importScripts() 异步加载脚本。// worker.js// ✅ 异步加载importScripts('utils.js', 'math.js');// ❌ 同步加载不可用const utils = require('./utils.js'); // 报错5. 无法使用某些全局对象Worker 的全局对象是 self(或 DedicatedWorkerGlobalScope),而不是 window。// Worker 中的全局对象console.log(self); // DedicatedWorkerGlobalScope// 可用的全局方法self.postMessage();self.onmessage;self.importScripts();self.close();// ❌ 不可用window.postMessage(); // 报错window.setTimeout(); // 报错(但 setTimeout 本身可用)可用的 API 和功能✅ Worker 中可用的功能// 网络请求fetch('/api/data');const xhr = new XMLHttpRequest();// 定时器setTimeout(() => {}, 1000);setInterval(() => {}, 1000);// 数据存储IndexedDB.open('myDB');// WebSocketconst ws = new WebSocket('ws://example.com');// Canvas(OffscreenCanvas)const canvas = new OffscreenCanvas(300, 150);// 加载其他脚本importScripts('helper.js');// 创建其他 Workerconst subWorker = new Worker('sub-worker.js');// 性能相关performance.now();performance.mark('start');限制的解决方案1. DOM 操作的替代方案// 主线程负责 DOM 操作const worker = new Worker('worker.js');worker.onmessage = function(e) { if (e.data.type === 'updateUI') { document.getElementById('result').textContent = e.data.value; }};// Worker 负责计算// worker.jsself.onmessage = function(e) { const result = heavyComputation(e.data); self.postMessage({ type: 'updateUI', value: result });};2. 数据存储的替代方案// 使用 IndexedDB(异步)// worker.jsconst request = indexedDB.open('myDB', 1);request.onsuccess = function() { const db = request.result; // 使用 db 进行存储操作};// 或者通过主线程传递数据// 主线程const data = localStorage.getItem('key');worker.postMessage({ data });// Workerself.onmessage = function(e) { const data = e.data.data; // 处理数据};3. 跨域限制的解决方案// 使用 CORS 头部// 服务器端设置Access-Control-Allow-Origin: *// 或者使用 Blob URLconst workerCode = fetch('https://other-domain.com/worker.js') .then(response => response.text()) .then(code => { const blob = new Blob([code], { type: 'application/javascript' }); const worker = new Worker(URL.createObjectURL(blob)); return worker; });性能考虑创建 Worker 的开销// ❌ 频繁创建和销毁 Worker(性能差)for (let i = 0; i < 1000; i++) { const worker = new Worker('worker.js'); worker.postMessage(i); worker.terminate();}// ✅ 复用 Worker(性能好)const worker = new Worker('worker.js');for (let i = 0; i < 1000; i++) { worker.postMessage(i);}消息传递的性能// ❌ 频繁传递大量数据(性能差)for (let i = 0; i < 10000; i++) { worker.postMessage({ data: largeArray[i] });}// ✅ 批量传递(性能好)worker.postMessage({ data: largeArray });// ✅ 使用 Transferable Objectsconst buffer = new ArrayBuffer(1024 * 1024);worker.postMessage({ buffer }, [buffer]);最佳实践Worker 只负责计算:将所有 DOM 操作放在主线程最小化消息传递:减少主线程和 Worker 之间的通信使用 Transferable Objects:对于大数据,使用转移而非拷贝复用 Worker:避免频繁创建和销毁错误处理:在 Worker 内部和主线程都添加错误处理清理资源:使用完毕后调用 worker.terminate()
阅读 0·2月21日 15:10

Swift 中的闭包是什么?什么是逃逸闭包和非逃逸闭包?

Swift 中的闭包是什么?闭包的捕获列表是什么?什么是逃逸闭包和非逃逸闭包?Swift 中的闭包是自包含的函数代码块,可以在代码中被传递和使用。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。闭包的基本概念:闭包是引用类型可以作为参数传递给函数可以作为函数的返回值可以存储在变量或常量中三种形式:全局函数、嵌套函数、闭包表达式闭包的语法:// 完整形式let greeting = { (name: String) -> String in return "Hello, \(name)"}// 简化形式let greeting = { name in "Hello, \(name)" }// 最简形式(使用参数缩写)let greeting: (String) -> String = { "Hello, \($0)" }捕获列表:用于显式声明闭包要捕获的变量使用 [weak self] 或 [unowned self] 避免循环引用使用 [weak var = weakVar] 捕获弱引用使用 [unowned var = var] 捕获无主引用示例: let closure = { [weak self] in self?.doSomething() }逃逸闭包:在函数返回后仍会被调用的闭包使用 @escaping 标记必须显式引用 self常用于异步操作、回调、网络请求示例: func request(completion: @escaping (Result) -> Void) { DispatchQueue.global().async { let result = fetchData() completion(result) } }非逃逸闭包:在函数返回前会被调用的闭包Swift 3.0+ 默认为非逃逸可以隐式引用 self性能更好,编译器可以优化示例: func process(_ closure: (Int) -> Void) { closure(42) }最佳实践:使用捕获列表避免循环引用优先使用非逃逸闭包合理使用尾随闭包语法使用 weak 或 unowned 处理 self 引用考虑闭包的内存开销
阅读 0·2月21日 15:10

Swift 中的字符串处理有哪些常用方法?如何进行字符串的拼接、截取、替换和查找?

Swift 中的字符串处理有哪些常用方法?如何进行字符串的拼接、截取、替换和查找?Swift 提供了丰富的字符串处理方法,包括拼接、截取、替换、查找等操作。Swift 的字符串类型是值类型,支持 Unicode 字符。字符串拼接:// 使用 + 运算符let str1 = "Hello"let str2 = "World"let combined = str1 + ", " + str2 // "Hello, World"// 使用 += 运算符var greeting = "Hello"greeting += ", World" // "Hello, World"// 使用字符串插值let name = "John"let age = 30let message = "My name is \(name) and I'm \(age) years old"// 使用 append 方法var text = "Hello"text.append(", World") // "Hello, World"字符串截取:let str = "Hello, World"// 使用 prefix 和 suffixlet prefix = str.prefix(5) // "Hello"let suffix = str.suffix(6) // "World"// 使用 dropFirst 和 dropLastlet withoutFirst = str.dropFirst() // "ello, World"let withoutLast = str.dropLast() // "Hello, Worl"// 使用索引let startIndex = str.startIndexlet endIndex = str.index(str.startIndex, offsetBy: 5)let substring = str[startIndex..<endIndex] // "Hello"// 使用 rangelet range = str.range(of: "World")!let substring2 = str[range] // "World"字符串替换:let str = "Hello, World"// 替换单个字符let replaced = str.replacingOccurrences(of: "World", with: "Swift")// "Hello, Swift"// 使用闭包替换let replaced2 = str.replacingOccurrences(of: "o", with: "O")// "HellO, WOrld"// 使用闭包进行复杂替换let replaced3 = str.replacingOccurrences(of: "[aeiou]", with: "", options: .regularExpression)// "Hll, Wrld"字符串查找:let str = "Hello, World"// 检查是否包含let containsHello = str.contains("Hello") // truelet containsSwift = str.contains("Swift") // false// 查找子字符串if let range = str.range(of: "World") { print("Found at range: \(range)")}// 查找前缀和后缀let hasPrefix = str.hasPrefix("Hello") // truelet hasSuffix = str.hasSuffix("World") // true// 查找字符let firstIndex = str.firstIndex(of: ",") // Optional(String.Index)字符串分割:let str = "apple,banana,orange"// 按字符分割let components = str.components(separatedBy: ",")// ["apple", "banana", "orange"]// 按字符集分割let whitespace = "Hello World"let words = whitespace.components(separatedBy: .whitespaces)// ["Hello", "World"]字符串大小写转换:let str = "Hello, World"let uppercased = str.uppercased() // "HELLO, WORLD"let lowercased = str.lowercased() // "hello, world"let capitalized = str.capitalized // "Hello, World"字符串修剪:let str = " Hello, World "let trimmed = str.trimmingCharacters(in: .whitespaces)// "Hello, World"let trimmed2 = str.trimmingCharacters(in: .whitespacesAndNewlines)// "Hello, World"字符串长度和字符:let str = "Hello, World"let count = str.count // 13let isEmpty = str.isEmpty // falselet firstChar = str.first // Optional("H")let lastChar = str.last // Optional("d")let chars = Array(str) // ["H", "e", "l", "l", "o", ",", " ", "W", "o", "r", "l", "d"]最佳实践:使用字符串插值提高可读性注意字符串索引的使用使用 contains、hasPrefix、hasSuffix 进行快速检查使用 replacingOccurrences 进行字符串替换注意 Unicode 字符的处理
阅读 0·2月21日 15:10

Swift 中的并发编程有哪些特性?如何使用 async/await 和 Actor?

Swift 中的并发编程有哪些特性?如何使用 async/await 和 Actor?Swift 5.5 引入了现代并发编程模型,包括 async/await、结构化并发、Actor 等特性,使并发编程更加安全和易用。async/await 基本用法:func fetchImage(from urlString: String) async throws -> UIImage { guard let url = URL(string: urlString) else { throw URLError(.badURL) } let (data, _) = try await URLSession.shared.data(from: url) guard let image = UIImage(data: data) else { throw URLError(.cannotDecodeRawData) } return image}Task { do { let image = try await fetchImage(from: "https://example.com/image.jpg") print("Image loaded: \(image)") } catch { print("Error: \(error)") }}async let 并发执行:func fetchUserData() async throws -> User { async let profile = fetchProfile() async let posts = fetchPosts() async let friends = fetchFriends() let (userProfile, userPosts, userFriends) = try await (profile, posts, friends) return User(profile: userProfile, posts: userPosts, friends: userFriends)}TaskGroup:func downloadImages(urls: [URL]) async throws -> [UIImage] { try await withThrowingTaskGroup(of: UIImage.self) { group in var images: [UIImage] = [] for url in urls { group.addTask { try await downloadImage(from: url) } } for try await image in group { images.append(image) } return images }}Actor:确保数据访问的线程安全防止数据竞争示例: actor Counter { private var value = 0 func increment() { value += 1 } func getValue() -> Int { return value } } let counter = Counter() await counter.increment() let count = await counter.getValue()MainActor:确保代码在主线程执行用于 UI 更新示例: @MainActor class ViewModel: ObservableObject { @Published var isLoading = false func loadData() async { isLoading = true let data = try? await fetchData() isLoading = false } }Task:创建异步任务可以取消示例: let task = Task { for i in 1...10 { print(i) try? await Task.sleep(nanoseconds: 1_000_000_000) } } // 取消任务 task.cancel()Continuation:将基于回调的 API 转换为 async/await示例: func fetchImageContinuation(from urlString: String) async throws -> UIImage { try await withCheckedThrowingContinuation { continuation in fetchImageCallback(from: urlString) { result in switch result { case .success(let image): continuation.resume(returning: image) case .failure(let error): continuation.resume(throwing: error) } } } }并发编程的最佳实践:使用 async/await 替代闭包回调使用 Actor 保护共享状态使用 MainActor 更新 UI使用 TaskGroup 处理并发任务正确处理任务取消
阅读 0·2月21日 15:10