Vue 3 WebSocket通信方案:从原理到实践

在现代Web应用开发中,实时通信已成为许多应用的核心需求。从即时聊天到实时数据更新,用户对应用响应速度的期望越来越高。本文将深入剖析一个Vue 3环境下的WebSocket通信方案,包括基础封装与实际应用,并详细对比WebSocket与传统轮询方案的差异。

一、为什么选择WebSocket?

在讨论具体实现之前,让我们先了解为什么WebSocket成为实时通信的首选方案。

1.1 实时通信的需求背景

随着Web应用的发展,传统的HTTP请求-响应模式已经无法满足某些场景的需求:

  • 即时聊天应用 - 需要消息即时送达
  • 实时数据监控 - 如股票行情、系统监控等需要实时更新
  • 多人协作工具 - 需要实时同步用户操作
  • 游戏应用 - 需要低延迟的实时交互

1.2 WebSocket的核心优势

WebSocket协议提供了一种在单个TCP连接上进行全双工通信的方式,相比传统HTTP请求具有以下显著优势:

  1. 双向通信 - 服务器可以主动向客户端推送数据,无需客户端先发起请求
  2. 低延迟 - 建立连接后,数据传输无需重新建立连接,减少了延迟
  3. 减少带宽消耗 - 相比HTTP请求,WebSocket头部信息更小,减少了数据传输量
  4. 保持连接状态 - 一次连接可维持长时间通信,无需重复的连接建立和认证过程
  5. 支持二进制数据 - 可以直接传输二进制数据,适用于音视频等场景

二、WebSocket与轮询的对比

为了更清晰地理解WebSocket的优势,我们将其与传统的轮询方案进行对比。

2.1 短轮询(Short Polling)

工作原理:客户端定期向服务器发送HTTP请求,询问是否有新数据,服务器立即响应,无论是否有新数据。

优点:实现简单,兼容性好

缺点

  • 大量的无效请求,浪费带宽和服务器资源
  • 实时性取决于轮询间隔,间隔太短增加服务器负载,间隔太长影响实时性
  • 每次请求都需要建立新的TCP连接,增加延迟

2.2 长轮询(Long Polling)

工作原理:客户端向服务器发送请求,服务器如果没有新数据则保持请求连接,直到有新数据或超时才响应,客户端收到响应后立即再次发起请求。

优点:比短轮询更高效,减少了部分无效请求

缺点

  • 服务器需要保持大量的并发连接,增加服务器压力
  • 仍有一定的延迟,且连接超时后需要重新建立
  • 每个请求仍包含完整的HTTP头部信息

2.3 WebSocket

工作原理:通过HTTP握手建立TCP连接后,保持连接状态,实现客户端与服务器之间的双向通信。

优点

  • 真正的双向通信,服务器可以主动推送数据
  • 减少了不必要的请求,节省带宽和服务器资源
  • 更低的延迟,实时性更好
  • 连接建立后,数据传输的头部信息非常小

缺点

  • 实现相对复杂
  • 某些网络环境可能有限制(如防火墙)
  • 旧浏览器兼容性问题(现代浏览器基本都已支持)

2.4 性能对比表

特性短轮询长轮询WebSocket
实时性
服务器负载
带宽消耗
实现复杂度
连接复用部分
双向通信

三、方案架构与文件关系

我们的WebSocket通信方案采用分层设计,由两个核心文件组成:

  1. useWebscoket.ts - 底层封装的Vue 3 Hook,提供基础的WebSocket功能
  2. webscoket.ts - 业务层封装,增强基础功能并提供更友好的API

这种分层设计使得WebSocket功能既具有良好的可复用性,又能灵活应对不同业务场景的需求。

四、核心实现分析

4.1 基础Hook封装 (useWebscoket.ts)

这个Hook采用了Vue 3的组合式API设计,主要实现了以下核心功能:

配置管理与默认值
// 默认配置
const defaultOptions: Partial<WebSocketOptions> = {retryTimes: 3,retryInterval: 3000,autoReconnect: true,withCredentials: false,timeout: 10000, // 默认超时时间10秒heartbeatInterval: 30000, // 默认心跳间隔30秒heartbeatMessage: JSON.stringify({ type: 'heartbeat', timestamp: Date.now() }),formatMessage: (data: string) => {try {return JSON.parse(data);} catch {return data;}},serializeMessage: (data: any) => {if (typeof data === 'string') {return data;}try {return JSON.stringify(data);} catch {return String(data);}},
};

这种设计允许用户在使用时只需提供必要的配置,同时保留了覆盖默认值的灵活性。

状态管理与连接控制

Hook通过多个ref来管理WebSocket的状态:

// 状态管理
const status = ref<WebSocketConnectionStatus>(WebSocketConnectionStatus.DISCONNECTED);
const webSocket = ref<WebSocket | null>(null);
const currentRetry = ref(0);
const error = ref<Error | null>(null);

并提供了连接、断开、重连等核心方法,方便用户控制WebSocket的生命周期。

自动重连机制

自动重连是保证WebSocket连接稳定性的关键特性:

// 处理重连逻辑
const handleReconnect = () => {if (!mergedOptions.autoReconnect || status.value === WebSocketConnectionStatus.RECONNECTING) {return;}// 检查是否超过最大重试次数if (mergedOptions.retryTimes !== undefined &&mergedOptions.retryTimes >= 0 &&currentRetry.value >= mergedOptions.retryTimes) {console.error('Max WebSocket reconnection attempts reached');return;}// 增加重试计数currentRetry.value += 1;status.value = WebSocketConnectionStatus.RECONNECTING;// 延迟重连reconnectTimer = window.setTimeout(() => {console.log(`WebSocket reconnecting... Attempt ${currentRetry.value}`);reconnect();}, mergedOptions.retryInterval);
};
心跳检测机制

心跳机制用于保持连接活跃并检测连接状态:

// 设置心跳检测
const setupHeartbeat = () => {if (!mergedOptions.heartbeatInterval || mergedOptions.heartbeatInterval <= 0) {return;}heartbeatTimer = window.setInterval(() => {if (webSocket.value &&status.value === WebSocketConnectionStatus.CONNECTED &&mergedOptions.heartbeatMessage) {// 支持函数类型的心跳消息,动态生成消息const heartbeatMsg = typeof mergedOptions.heartbeatMessage === 'function' ? mergedOptions.heartbeatMessage() : mergedOptions.heartbeatMessage;send(heartbeatMsg);}}, mergedOptions.heartbeatInterval);
};

4.2 业务层增强 (webscoket.ts)

业务层封装在基础Hook之上,进一步增强了WebSocket的功能:

消息队列管理

消息队列解决了连接未就绪时的消息发送问题:

// 创建一个队列存储需要发送的消息
const messageQueue: Array<{ data: any; callback?: (success: boolean) => void }> = [];
let isConnected = false;// 连接成功时,发送队列中的消息
open: (event) => {console.log('✨ WebSocket连接成功! 事件信息:', event);isConnected = true;lastHeartbeatResponseTime = Date.now();console.log(`💬 待发送消息队列中有 ${messageQueue.length} 条消息`);// 发送队列中的所有消息while (messageQueue.length > 0) {const { data, callback } = messageQueue.shift()!;console.log(`📤 发送队列中的消息:`, data);try {webSocketService.send(data);console.log(`✅ 消息发送成功`);callback?.(true);} catch (error) {console.error('❌ 发送消息失败:', error);callback?.(false);}}
},
增强的发送方法

增强的发送方法会自动检测连接状态并管理消息队列:

// 包装send方法,支持连接成功后自动发送
const enhancedSend = (data: any, callback?: (success: boolean) => void) => {console.log(`📤 尝试发送消息:`, data);console.log(`🔌 当前连接状态: ${isConnected ? '已连接' : '未连接'}`);if (isConnected) {try {webSocketService.send(data);console.log(`✅ 消息发送成功`);callback?.(true);} catch (error) {console.error('❌ 发送消息失败:', error);callback?.(false);}} else {// 将消息加入队列,等待连接成功后发送messageQueue.push({ data, callback });console.log(`📋 消息已加入队列,当前队列长度: ${messageQueue.length}`);}
};

五、核心技术点解析

5.1 状态管理

该方案使用了明确的状态枚举来表示WebSocket的各种状态:

// WebSocket连接状态
export enum WebSocketConnectionStatus {DISCONNECTED = 'disconnected',CONNECTING = 'connecting',CONNECTED = 'connected',ERROR = 'error',RECONNECTING = 'reconnecting',
}

这种方式使状态管理更加清晰和类型安全。

5.2 动态心跳消息

方案支持函数类型的心跳消息,每次发送时动态生成最新时间戳,避免服务器认为连接不活跃:

heartbeatMessage: () => JSON.stringify({ type: 'heartbeat', timestamp: Date.now() })

5.3 错误处理与重试策略

方案实现了智能的错误处理和重试策略:

  1. 限制最大重试次数,避免无限循环重连
  2. 根据错误类型和关闭码决定是否需要重连
  3. 增加重试间隔,避免频繁重连导致的资源浪费

5.4 二进制消息支持

除了文本消息外,该方案还支持二进制消息的处理:

if (event.data instanceof Blob) {// 处理二进制数据const reader = new FileReader();reader.onload = (e) => {if (e.target?.result) {const textData = e.target.result.toString();try {const parsedData = mergedOptions.formatMessage?.(textData) ?? textData;if (mergedOptions.eventHandlers?.message) {mergedOptions.eventHandlers.message(parsedData, event);}} catch (err) {console.error('Failed to process WebSocket binary message:', err);error.value =err instanceof Error? err: new Error('Failed to process WebSocket binary message');}}};reader.readAsText(event.data);
}

六、使用场景与最佳实践

6.1 使用场景

该WebSocket方案适用于以下场景:

  1. 实时聊天应用 - 需要实时收发消息
  2. 实时数据更新 - 如股票行情、监控数据等
  3. 多人协作工具 - 需要实时同步协作状态
  4. 通知系统 - 推送实时通知

6.2 最佳实践

  1. 合理设置心跳间隔 - 根据应用特性和服务器要求调整心跳间隔
  2. 设置重试策略 - 避免无限重试,合理设置最大重试次数
  3. 消息队列管理 - 注意处理队列积压问题,避免内存泄漏
  4. 错误处理 - 实现完善的错误处理和用户反馈机制
  5. 资源释放 - 在组件卸载时正确断开连接,释放资源

七、完整源码

useWebscoket.ts


import type { ComputedRef, Ref } from 'vue';
import { computed, onUnmounted, ref, watch } from 'vue';// WebSocket事件处理函数接口
export interface WebSocketEventHandlers {// 消息接收事件处理函数message?: (data: any, event: MessageEvent) => void;// 连接建立事件处理函数open?: (event: Event) => void;// 连接错误事件处理函数error?: (event: Event) => void;// 连接关闭事件处理函数close?: (event: CloseEvent) => void;// 自定义事件处理函数[eventType: string]:| ((data: any, event: MessageEvent) => void)| ((event: Event) => void)| ((event: CloseEvent) => void)| undefined;
}// WebSocket配置接口
export interface WebSocketOptions {url: string;// 重试次数,-1表示无限重试retryTimes?: number;// 重试间隔(毫秒)retryInterval?: number;// 是否自动重连autoReconnect?: boolean;// 事件处理函数eventHandlers?: WebSocketEventHandlers;// withCredentials选项withCredentials?: boolean;// 连接超时时间(毫秒)timeout?: number;// 心跳检测间隔(毫秒),0表示不启用heartbeatInterval?: number;// 心跳消息heartbeatMessage?: string | object | (() => string | object);// 格式化消息数据formatMessage?: (data: string) => any;// 序列化发送数据serializeMessage?: (data: any) => string;// 子协议protocols?: string | string[];
}// WebSocket连接状态
export enum WebSocketConnectionStatus {DISCONNECTED = 'disconnected',CONNECTING = 'connecting',CONNECTED = 'connected',ERROR = 'error',RECONNECTING = 'reconnecting',
}// WebSocket hook返回值接口
export interface WebSocketResult {// 连接状态status: Ref<WebSocketConnectionStatus>;// 连接的WebSocket实例webSocket: Ref<WebSocket | null>;// 当前重试次数currentRetry: Ref<number>;// 错误信息error: Ref<Error | null>;// 开始连接connect: () => void;// 断开连接disconnect: () => void;// 重新连接reconnect: () => void;// 发送消息send: (data: string | ArrayBuffer | Blob | object) => void;
}/*** 创建一个基于WebSocket的连接hook* @param options WebSocket配置项* @returns WebSocket操作对象*/
export const useWebSocket = (options: WebSocketOptions): WebSocketResult => {// 默认配置const defaultOptions: Partial<WebSocketOptions> = {retryTimes: 3,retryInterval: 3000,autoReconnect: true,withCredentials: false,timeout: 10000, // 默认超时时间10秒heartbeatInterval: 30000, // 默认心跳间隔30秒heartbeatMessage: JSON.stringify({ type: 'heartbeat', timestamp: Date.now() }),formatMessage: (data: string) => {try {return JSON.parse(data);} catch {return data;}},serializeMessage: (data: any) => {if (typeof data === 'string') {return data;}try {return JSON.stringify(data);} catch {return String(data);}},};// 合并用户配置和默认配置const mergedOptions = { ...defaultOptions, ...options };// 状态管理const status = ref<WebSocketConnectionStatus>(WebSocketConnectionStatus.DISCONNECTED);const webSocket = ref<WebSocket | null>(null);const currentRetry = ref(0);const error = ref<Error | null>(null);let reconnectTimer: number | null = null;let heartbeatTimer: number | null = null;let connectionTimeout: number | null = null;// 清除所有定时器const clearTimers = () => {if (reconnectTimer) {clearTimeout(reconnectTimer);reconnectTimer = null;}if (heartbeatTimer) {clearInterval(heartbeatTimer);heartbeatTimer = null;}if (connectionTimeout) {clearTimeout(connectionTimeout);connectionTimeout = null;}};// 设置心跳检测const setupHeartbeat = () => {if (!mergedOptions.heartbeatInterval || mergedOptions.heartbeatInterval <= 0) {return;}heartbeatTimer = window.setInterval(() => {if (webSocket.value &&status.value === WebSocketConnectionStatus.CONNECTED &&mergedOptions.heartbeatMessage) {// 支持函数类型的心跳消息,动态生成消息const heartbeatMsg = typeof mergedOptions.heartbeatMessage === 'function' ? mergedOptions.heartbeatMessage() : mergedOptions.heartbeatMessage;send(heartbeatMsg);}}, mergedOptions.heartbeatInterval);};// 创建WebSocket连接const createWebSocket = (): WebSocket => {status.value = WebSocketConnectionStatus.CONNECTING;const ws = new WebSocket(mergedOptions.url);// 设置连接超时if (mergedOptions.timeout && mergedOptions.timeout > 0) {connectionTimeout = window.setTimeout(() => {if (status.value === WebSocketConnectionStatus.CONNECTING) {error.value = new Error('Connection timeout');status.value = WebSocketConnectionStatus.ERROR;ws.close();handleReconnect();}}, mergedOptions.timeout);}// 设置message事件监听ws.onmessage = (event) => {if (connectionTimeout) {clearTimeout(connectionTimeout);connectionTimeout = null;}try {let data;if (event.data instanceof Blob) {// 处理二进制数据const reader = new FileReader();reader.onload = (e) => {if (e.target?.result) {const textData = e.target.result.toString();try {const parsedData = mergedOptions.formatMessage?.(textData) ?? textData;if (mergedOptions.eventHandlers?.message) {mergedOptions.eventHandlers.message(parsedData, event);}} catch (err) {console.error('Failed to process WebSocket binary message:', err);error.value =err instanceof Error? err: new Error('Failed to process WebSocket binary message');}}};reader.readAsText(event.data);} else {// 处理文本数据data = mergedOptions.formatMessage?.(event.data as string) ?? event.data;if (mergedOptions.eventHandlers?.message) {mergedOptions.eventHandlers.message(data, event);}}} catch (err) {console.error('Failed to process WebSocket message:', err);error.value = err instanceof Error ? err : new Error('Failed to process WebSocket message');}};// 设置open事件监听ws.onopen = (event) => {if (connectionTimeout) {clearTimeout(connectionTimeout);connectionTimeout = null;}console.log('WebSocket connection established');status.value = WebSocketConnectionStatus.CONNECTED;currentRetry.value = 0;error.value = null;// 设置心跳检测setupHeartbeat();if (mergedOptions.eventHandlers?.open) {mergedOptions.eventHandlers.open(event);}};// 设置error事件监听ws.onerror = (event) => {if (connectionTimeout) {clearTimeout(connectionTimeout);connectionTimeout = null;}console.error('WebSocket connection error:', event);status.value = WebSocketConnectionStatus.ERROR;error.value = new Error('WebSocket connection error');if (mergedOptions.eventHandlers?.error) {mergedOptions.eventHandlers.error(event);}// 处理自动重连handleReconnect();};// 设置close事件监听ws.onclose = (event) => {if (connectionTimeout) {clearTimeout(connectionTimeout);connectionTimeout = null;}console.log('WebSocket connection closed:', event.code, event.reason);// 如果不是手动关闭的连接,尝试重连if (status.value !== WebSocketConnectionStatus.DISCONNECTED) {status.value = WebSocketConnectionStatus.DISCONNECTED;handleReconnect();}if (mergedOptions.eventHandlers?.close) {mergedOptions.eventHandlers.close(event);}};return ws;};// 处理重连逻辑const handleReconnect = () => {if (!mergedOptions.autoReconnect || status.value === WebSocketConnectionStatus.RECONNECTING) {return;}// 检查是否超过最大重试次数if (mergedOptions.retryTimes !== undefined &&mergedOptions.retryTimes >= 0 &&currentRetry.value >= mergedOptions.retryTimes) {console.error('Max WebSocket reconnection attempts reached');return;}// 增加重试计数currentRetry.value += 1;status.value = WebSocketConnectionStatus.RECONNECTING;// 延迟重连reconnectTimer = window.setTimeout(() => {console.log(`WebSocket reconnecting... Attempt ${currentRetry.value}`);reconnect();}, mergedOptions.retryInterval);};// 连接WebSocketconst connect = () => {// 先断开已有的连接disconnect();try {webSocket.value = createWebSocket();} catch (err) {console.error('Failed to create WebSocket connection:', err);error.value = err instanceof Error ? err : new Error('Failed to create WebSocket connection');status.value = WebSocketConnectionStatus.ERROR;handleReconnect();}};// 断开WebSocket连接const disconnect = () => {clearTimers();if (webSocket.value) {webSocket.value.close();webSocket.value = null;}status.value = WebSocketConnectionStatus.DISCONNECTED;console.log('WebSocket connection closed');};// 重新连接WebSocketconst reconnect = () => {disconnect();connect();};// 发送消息const send = (data: string | ArrayBuffer | Blob | object) => {if (!webSocket.value || status.value !== WebSocketConnectionStatus.CONNECTED) {console.error('Cannot send message: WebSocket is not connected');return;}try {if (typeof data === 'string' || data instanceof ArrayBuffer || data instanceof Blob) {webSocket.value?.send(data);} else {// 序列化对象const serializedData = mergedOptions.serializeMessage?.(data) ?? JSON.stringify(data);webSocket.value?.send(serializedData);}} catch (err) {console.error('Failed to send WebSocket message:', err);error.value = err instanceof Error ? err : new Error('Failed to send WebSocket message');}};// 组件卸载时断开连接onUnmounted(() => {disconnect();});return {status,webSocket,currentRetry,error,connect,disconnect,reconnect,send,};
};export default useWebSocket;

webscoket.ts

import commonConfig from '@/commonConfig';
import useWebSocket from '@/components/hooks/useWebscoket';console.log('初始化WebSocket服务,连接地址:', commonConfig.webSocketUrl);// 创建一个队列存储需要发送的消息
const messageQueue: Array<{ data: any; callback?: (success: boolean) => void }> = [];
let isConnected = false;
let lastHeartbeatResponseTime = 0;// 创建WebSocket实例
const webSocketService = useWebSocket({url: commonConfig.webSocketUrl,retryTimes: 5, // 限制最大重试次数为5次retryInterval: 5000, // 重试间隔5秒autoReconnect: true, // 自动重连heartbeatInterval: 30000, // 心跳间隔30秒heartbeatMessage: () => JSON.stringify({ type: 'heartbeat', timestamp: Date.now() }), // 动态生成心跳消息eventHandlers: {// 连接成功时,发送队列中的消息open: (event) => {console.log('✨ WebSocket连接成功! 事件信息:', event);isConnected = true;lastHeartbeatResponseTime = Date.now();console.log(`💬 待发送消息队列中有 ${messageQueue.length} 条消息`);// 发送队列中的所有消息while (messageQueue.length > 0) {const { data, callback } = messageQueue.shift()!;console.log(`📤 发送队列中的消息:`, data);try {webSocketService.send(data);console.log(`✅ 消息发送成功`);callback?.(true);} catch (error) {console.error('❌ 发送消息失败:', error);callback?.(false);}}},// 连接关闭时close: (event) => {console.log('🚫 WebSocket连接关闭! 关闭码:', event.code, '原因:', event.reason);isConnected = false;// 根据关闭码判断是否需要重连const shouldAutoReconnect = event.code !== 1000 || event.reason !== 'Bye';console.log(`🔄 自动重连状态: ${shouldAutoReconnect ? '开启' : '关闭'}`);},// 连接错误时error: (event) => {console.error('❌ WebSocket连接错误:', event);},// 接收消息时message: (data, event) => {console.log('📥 接收到WebSocket消息:', data);// 更新心跳响应时间if (data && typeof data === 'object' && data.type === 'heartbeat') {lastHeartbeatResponseTime = Date.now();console.log('❤️ 收到心跳响应,连接正常');}},},
});// 包装send方法,支持连接成功后自动发送
const enhancedSend = (data: any, callback?: (success: boolean) => void) => {console.log(`📤 尝试发送消息:`, data);console.log(`🔌 当前连接状态: ${isConnected ? '已连接' : '未连接'}`);if (isConnected) {try {webSocketService.send(data);console.log(`✅ 消息发送成功`);callback?.(true);} catch (error) {console.error('❌ 发送消息失败:', error);callback?.(false);}} else {// 将消息加入队列,等待连接成功后发送messageQueue.push({ data, callback });console.log(`📋 消息已加入队列,当前队列长度: ${messageQueue.length}`);}
};// 启动连接
webSocketService.connect();// 导出增强版的WebSocket服务
export default {...webSocketService,send: enhancedSend,// 暴露连接状态和队列信息,便于调试isConnected: () => isConnected,getMessageQueueLength: () => messageQueue.length,
};

八、总结

本文深入剖析了一个基于Vue 3的WebSocket通信方案,从为什么选择WebSocket开始,对比了WebSocket与传统轮询方案的差异,然后详细解析了其核心技术点和实现原理。

WebSocket相比传统的轮询方案,在实时性、性能和用户体验方面都有显著优势,特别适合需要实时通信的Web应用。而我们的方案通过分层设计,既提供了基础的WebSocket功能,又通过业务层增强提供了更友好的API,使开发者能够更方便地在Vue 3应用中集成WebSocket通信功能。

这个方案具有以下优势:

  1. 高度封装 - 通过Hook和服务层两级封装,提供了简洁易用的API
  2. 稳定性强 - 实现了完善的重连机制、心跳检测和错误处理
  3. 灵活性高 - 支持丰富的配置选项,可适应不同的业务场景
  4. 类型安全 - 充分利用TypeScript的类型系统,提供良好的类型提示
  5. 用户友好 - 实现了消息队列,解决了连接未就绪时的消息发送问题

通过合理的封装和抽象,可以大大提高代码的可维护性和复用性,为构建稳定可靠的实时应用奠定基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95296.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95296.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/95296.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows 电源管理和 Shutdown 命令详解

一、Windows 电源管理概述 Windows 操作系统通过其内置的电源管理框架&#xff0c;为用户提供了多种电源状态和配置选项&#xff0c;以在性能、能耗和数据安全之间找到最佳平衡点。以下是 Windows 系统中常见的电源状态及其特点&#xff1a; 1. 睡眠&#xff08;Sleep&#xff…

Selenium WebUI 自动化“避坑”指南——从常用 API 到 10 大高频问题

目录 一、为什么 90% 的 UI 自动化脚本活不过 3 个月&#xff1f; 二、Selenium必会 API 速查 三、实践 四、10 大高频异常“症状 → 病因 → 处方” 五、可复用的工具函数 六、面试高频追问&#xff08;附标准答案&#xff09; 一、为什么 90% 的 UI 自动化脚本活不过 …

【微信小程序】微信小程序基于双token的API请求封装与无感刷新实现方案

文章目录前言一、设计思路二、执行流程三、核心模块3.1 全局配置3.2 request封装3.2.1 request方法配置参数3.2.2 请求预处理3.2.3 核心请求流程3.3 刷新accessToken3.4 辅助方法四、api封装示例总结前言 现代前后端分离的模式中&#xff0c;一般都是采用token的方式实现API的…

基于单片机醉酒驾驶检测系统/酒精检测/防疲劳驾驶设计

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 该设计基于单片机开发&#xff0c;旨在通过实时检测驾驶员酒精浓度&#xff0c;预防酒后驾驶行为…

第6章:垃圾回收分析与调优

1. 垃圾回收基础 1.1 Java 垃圾回收概述 垃圾回收&#xff08;Garbage Collection&#xff0c;GC&#xff09;是 Java 虚拟机自动内存管理的核心机制。理解 GC 的工作原理对于 Java 应用性能调优至关重要。 1.1.1 垃圾回收的目标 自动内存管理&#xff1a;无需手动释放内存防止…

ROS2核心模块-动作通信、参数服务

动作通信 机器人导航到某个目标点,此过程需要一个节点A发布目标信息&#xff0c;然后一个节点B接收到请求并控制移动&#xff0c;最终响应目标达成状态信息。 乍一看&#xff0c;这好像是服务通信实现&#xff0c;因为需求中要A发送目标&#xff0c;B执行并返回结果&#xff0c…

word文档封面中文件编号等标题和内容无法对齐

问题 word文档封面中文件编号等标题和内容无法对齐&#xff0c;因为标题使用的是底纹不是文件内容。 解决办法 字体大小、行距两者配合就可以解决。

163起融资,梅卡曼德融资额夺冠,钉钉、百度智能云10周年,汉桑科技IPO| 2025年8月人工智能投融资观察 · 极新月报

“ 二级的活跃会传导到一级吗&#xff1f;”文&#xff5c;云舒&小鱼编辑 | 小白出品&#xff5c;极新8月重点关注&#xff1a;1、八月人工智能领域投融资事件163起&#xff0c;披露金额76.8亿人民币。2、亿级人民币以上金额的投资事件共20起 。3、八月人工智能领域发生一起…

微信小程序预览和分享文件

预览文档previewFile(val) { let item val.currentTarget.dataset.item wx.downloadFile({url: item.filePath, // 替换为实际的文件地址success: function (res) {let filePath ${wx.env.USER_DATA_PATH}/${item.fileName}|| res.tempFilePath //查看的文件名wx.openDocumen…

开源 C++ QT Widget 开发(十二)图表--环境监测表盘

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源…

ARMv8架构01 - ARM64架构寄存器基础

一 、ARM64架构基础 1 ARMv8 A 架构介绍 ARMv8 - A是ARM公司发布的第一代支持64位处理器的指令集和架构。它在扩充64位寄存器的同时提供对上一代架构指令集的兼容&#xff0c;因而能同时提供运行 32位 和 64位应用程序的执行环境。 超大物理地址空间&#xff08;large Physical…

flutter专栏--深入剖析你的第一个flutter应用

使用fvm管理flutter版本 如果你有使用多版本flutter的需求&#xff0c;那么fvm将会给你提供较大的帮助。下面我列举一下mac flutter3.35.2的版本的操作命令&#xff0c;完成之后&#xff0c;你将可以随意切换flutter版本 # 下载fvm相关的依赖 brew tap leoafarias/fvm brew …

MongoDB 聚合查询超时:索引优化与分片策略的踩坑记录

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 &#x1f31f; Hello&#xff0c;我是Xxtaoaooo&#xff01; &#x1f308; “代码是逻辑的诗篇&#xff…

Augmentcode免费额度AI开发WordPress商城实战

Augment AI开发WordPress商城实战&#xff1a;从零构建到免费额度续杯完整指南 前言 在AI编程工具日益普及的今天&#xff0c;如何高效利用这些工具来开发实际项目成为了开发者关注的焦点。本文将详细介绍如何使用Augment AI从零开始构建一个功能完整的WordPress商城系统&#…

【C++八股文】数据结构篇

一、单例模式优化实现 原代码问题分析 ​内存序重排序风险​&#xff1a;双重检查锁在C中可能因指令重排导致半初始化对象被访问​锁粒度过大​&#xff1a;每次获取实例都需要加锁&#xff0c;影响性能​线程安全性不足​&#xff1a;未考虑C11前的内存模型问题 改进方案&a…

并发编程——15 线程池ForkJoinPool实战及其工作原理分析

1 一道算法题引发的思考及其实现 1.1 算法题 问&#xff1a;如何充分利用多核 CPU 的性能&#xff0c;快速对一个2千万大小的数组进行排序&#xff1f; 这道题可以通过归并排序来解决&#xff1b; 1.2 什么是归并排序&#xff1f; 归并排序&#xff08;Merge Sort&#xff…

Kafka面试精讲 Day 6:Kafka日志存储结构与索引机制

【Kafka面试精讲 Day 6】Kafka日志存储结构与索引机制 在“Kafka面试精讲”系列的第6天&#xff0c;我们将深入剖析 Kafka的日志存储结构与索引机制。这是Kafka高性能、高吞吐量背后的核心设计之一&#xff0c;也是中高级面试中的高频考点。面试官常通过这个问题考察候选人是否…

Linux 字符设备驱动框架学习记录(三)

Linux字符设备驱动开发新框架详解 一、新旧驱动框架对比 传统字符设备驱动流程 手动分配设备号 (register_chrdev_region)实现file_operations结构体使用mknod手动创建设备节点 新式驱动框架优势 自动设备号分配&#xff1a;动态申请避免冲突自动节点创建&#xff1a;通过class…

《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(1)

目 录 摘 要 一、研究背景与目的 1.1 介绍拒绝服务&#xff08;DoS&#xff09;和分布式拒绝服务&#xff08;DDoS&#xff09;攻击的背景 &#xff08;1&#xff09;拒绝服务攻击&#xff08;DoS&#xff09;  &#xff08;2&#xff09;分布式拒绝服务攻击&#xff0…

深度学习篇---模型组成部分

模型组成部分&#xff1a;在 PyTorch 框架下进行图像分类任务时&#xff0c;深度学习代码通常由几个核心部分组成。这些部分中有些可以在不同网络间复用&#xff0c;有些则需要根据具体任务或网络结构进行修改。下面我将用通俗易懂的方式介绍这些组成部分&#xff1a;1. 数据准…