1 基本对象
1.1 DOCUMENT
定义:浏览器内置的全局对象(window.document),提供访问和操作 HTML 文档的接口。
核心功能:
- 查找和选择 HTML 元素(如 div、input)。
- 修改元素的内容、属性和样式。
- 创建新元素并添加到文档中。
- 监听用户事件(如点击、滚动)
1. 查找和选择HTML元素
// 通过ID获取元素(返回单个元素)
const elementById = document.getElementById('my-element');
// 通过标签名获取元素(返回HTMLCollection)
const allDivs = document.getElementsByTagName('div');
console.log(allDivs[0]); // 访问第一个div
// 通过类名获取元素(返回HTMLCollection)
const elementsByClass = document.getElementsByClassName('my-class');
// 通过CSS选择器获取元素(返回单个元素)
const firstParagraph = document.querySelector('p');
const specificElement = document.querySelector('#container .item');
// 通过CSS选择器获取所有匹配元素(返回NodeList)
const allParagraphs = document.querySelectorAll('p');
allParagraphs.forEach(p => {
console.log(p.textContent);
});
一般而言,其实我感觉你只需要了解第一个就行 id(针对后端开发人员)
2.监听用户事件(如点击、滚动)
// 获取按钮元素
const button = document.getElementById('my-button');
// 添加点击事件监听
button.addEventListener('click', (event) => {
console.log('Button clicked!');
console.log('Event target:', event.target); // 触发事件的元素
console.log('Current element:', event.currentTarget); // 绑定事件的元素
// 修改元素
event.target.textContent = 'Clicked!';
});
// 监听表单提交
const form = document.getElementById('my-form');
form.addEventListener('submit', (event) => {
event.preventDefault(); // 阻止表单默认提交行为
const inputValue = form.querySelector('input').value;
console.log('Form submitted with value:', inputValue);
});
// 监听键盘事件
document.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
console.log('Enter key pressed');
}
});
脚本这种语言 是动态类型 所有是解释型语言,就是无需定于类型 这段代码涉及到一个内置对象 event 先别管这些
1.2 EVENT
event
是 JavaScript 中的内置事件对象,当浏览器检测到用户操作(如点击、键盘输入、滚动等)时,会自动创建该对象并传递给事件处理函数。它包含了事件的所有相关信息,是处理交互逻辑的核心工具。
一、event 对象的本质:浏览器自动生成的内置对象
创建时机
当事件(如click
、keydown
、mousemove
)触发时,浏览器会立即生成event
对象,并作为参数传入事件处理函数。例如:button.addEventListener('click', (event) => { // 这里的 event 就是浏览器自动创建的事件对象 console.log(event); // 输出事件对象的所有属性和方法 });
内置特性
event
对象是浏览器内置的Event
类的实例,包含事件类型、目标元素、交互数据等核心信息,无需手动创建,直接使用即可。
二、event 对象的核心用途:封装事件相关的所有信息
- 标识事件源(目标元素)
event.target
:触发事件的具体元素(如被点击的按钮)。event.currentTarget
:绑定事件监听器的元素(常用于事件委托)。
// 给父元素绑定点击事件
document.getElementById('parent').addEventListener('click', (event) => {
console.log('event.target:', event.target.id); // 输出:child(实际被点击的按钮)
console.log('event.currentTarget:', event.currentTarget.id); // 输出:parent(绑定事件的父元素)
});
- 获取交互数据
- 鼠标事件:
event.clientX
(鼠标水平坐标)、event.offsetY
(相对于目标元素的垂直坐标)。 - 键盘事件:
event.key
(按下的键名,如'Enter'
)、event.keyCode
(键的编码)。 - 表单事件:
event.target.value
(输入框的值)、event.target.checked
(复选框的勾选状态)。
- 控制事件行为
event.preventDefault()
:阻止事件的默认行为(如阻止链接跳转、表单提交)。event.stopPropagation()
:阻止事件冒泡到父元素(中断事件传播)。
三、不同事件类型的 event 对象差异
event
对象的属性会根据事件类型动态变化,以下是常见场景:
事件类型 | 特有属性 | 示例场景 |
---|---|---|
点击事件(click) | event.clientX , event.clientY |
计算鼠标点击位置 |
键盘事件(keydown) | event.key , event.ctrlKey , event.shiftKey |
判断用户是否按下组合键 |
表单输入(input) | event.target.value |
实时获取输入框内容 |
鼠标移动(mousemove) | event.pageX , event.pageY |
实现拖拽效果、跟随鼠标的元素 |
窗口滚动(scroll) | event.target.scrollTop |
监听页面滚动位置,实现导航栏动态效果 |
音视频事件(play/pause) | event.target.duration (视频总时长) |
视频播放时更新进度条 |
四、在音视频开发中的典型应用
- 视频进度条拖拽
const progressBar = document.getElementById('progress-bar');
const video = document.getElementById('my-video');
progressBar.addEventListener('click', (event) => {
// 计算点击位置占进度条的比例
const rect = progressBar.getBoundingClientRect();
const clickX = event.clientX - rect.left;
const percentage = (clickX / rect.width) * 100;
// 转换为视频播放时间
const seekTime = (percentage / 100) * video.duration;
video.currentTime = seekTime;
});
- 键盘控制视频播放
document.addEventListener('keydown', (event) => {
const video = document.getElementById('my-video');
if (event.key === ' ' || event.key === 'Spacebar') {
event.preventDefault(); // 阻止空格滚动页面的默认行为
video.paused ? video.play() : video.pause();
} else if (event.key === 'ArrowRight') {
video.currentTime += 10; // 快进10秒
} else if (event.key === 'ArrowLeft') {
video.currentTime -= 5; // 快退5秒
}
});
总结:event 对象的核心地位
作为浏览器内置的事件对象,event
是 JavaScript 交互逻辑的“信息枢纽”:它封装了用户操作的所有细节,让开发者能够精准响应交互行为。无论是简单的按钮点击,还是复杂的音视频手势控制,熟练掌握 event
的属性和方法都是实现高效交互的基础。建议通过实际项目练习(如自定义视频播放器的控制逻辑)来深入理解其应用场景!
1.3 navigator
navigator 是浏览器提供的一个全局对象,用于访问浏览器相关信息和功能。在音视频开发中,它是获取摄像头、麦克风等媒体设备的入口。
1.获取摄像头
// 请求访问摄像头和麦克风
async function accessCameraAndMicrophone() {
try {
// 配置约束条件(分辨率、帧率等)
const constraints = {
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
frameRate: { ideal: 30 }
},
audio: true
};
// 获取媒体流
const stream = await navigator.mediaDevices.getUserMedia(constraints);//等待异步返回结果
// 渲染到video元素
const videoElement = document.getElementById('my-video');
videoElement.srcObject = stream;
return stream;
} catch (error) {
console.error('获取媒体失败:', error);
// 处理错误(如权限被拒、设备不可用)
}
}
NotFoundError:未找到指定设备。
NotAllowedError:用户拒绝授予权限。
OverconstrainedError:设备不支持请求的约束条件。
1.2 共享屏幕
async function startScreenShare() {
try {
// 配置约束条件
const constraints = {
video: true,
audio: false // 可选:是否捕获系统音频
};
// 获取屏幕共享流
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
// 渲染到video元素
const videoElement = document.getElementById('screen-share');
videoElement.srcObject = stream;
return stream;
} catch (error) {
console.error('屏幕共享失败:', error);
}
}
2 WEBRTC
2.1 基本概念
WebRTC(Web Real-Time Communication)是现代浏览器提供的实时音视频通信 API,让网页能直接通过浏览器进行视频通话、语音聊天、文件共享等功能,无需安装插件。
获取媒体流
使用 navigator.mediaDevices.getUserMedia()
方法来获取摄像头和麦克风的媒体流。示例代码如下:
const constraints = { video: true, audio: true };
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('Got MediaStream:', stream);
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
constraints
对象用于指定需要获取的媒体类型,如视频和音频。成功获取到媒体流后,可以将其设置到 video
元素的 srcObject
属性上进行展示。
RTCPeerConnection
- 创建连接:通过
new RTCPeerConnection()
来创建一个点对点连接对象。 - 媒体协商:使用
createOffer()
和createAnswer()
方法来生成会话描述(Offer 和 Answer),并通过信令服务器在对等方之间交换这些描述,以协商双方支持的媒体格式、编码等参数。 - 添加媒体流:使用
addTrack()
方法将本地媒体流添加到RTCPeerConnection
对象中,以便将其发送到对等方。 - 监听事件:通过监听
icecandidate
事件来收集本地的 ICE 候选地址,并将其发送给对等方;监听track
事件来接收对等方发送的媒体流,以便在本地进行展示。
RTCSessionDescription
- 生成与设置:在媒体协商过程中,
RTCPeerConnection
对象会生成RTCSessionDescription
对象,包含会话的元数据,如媒体类型、编码格式等。使用setLocalDescription()
方法将本地生成的会话描述设置到RTCPeerConnection
对象中,使用setRemoteDescription()
方法来设置接收到的对等方的会话描述。
RTCDataChannel
- 创建通道:通过
RTCPeerConnection
对象的createDataChannel()
方法来创建一个数据通道,用于传输文本、二进制数据等。 - 发送与接收数据:使用
send()
方法发送数据,通过监听message
事件来接收对等方发送的数据。
2.2 实战
一、RTCPeerConnection:建立音视频连接的核心引擎
1. 核心作用
- 管理点对点连接,处理音视频数据传输
- 协调媒体协商(Offer/Answer)和网络穿透(ICE)
- 绑定媒体流(摄像头、麦克风)和数据通道
2. 关键用法(极简流程)
配置与创建连接
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
添加本地媒体流
const stream = await navigator.mediaDevices.getUserMedia({ video: true }); stream.getTracks().forEach(track => pc.addTrack(track, stream));
媒体协商(Offer/Answer)
// 发起方生成 Offer const offer = await pc.createOffer(); await pc.setLocalDescription(offer); // 接收方根据 Offer 生成 Answer await pc.setRemoteDescription(offer); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer);
交换 ICE 候选者
// 发送本地候选者 pc.onicecandidate = (event) => { if (event.candidate) sendToServer(event.candidate); }; // 接收远程候选者 await pc.addIceCandidate(remoteCandidate);
接收远程媒体流
pc.ontrack = (event) => { remoteVideo.srcObject = event.streams[0]; };
3. 流程图(简化版)
┌──────────────────────────────────────────────────────────┐
│ RTCPeerConnection │
├──────────────────────────────────────────────────────────┤
│ 1. 配置 STUN/TURN 服务器 │
│ 2. 创建连接实例 │
│ 3. 添加本地媒体流(摄像头/麦克风) │
│ 4. 生成 Offer 并发送给对方 │
│ 5. 接收对方的 Answer 并设置为远程描述 │
│ 6. 收集并交换 ICE 候选者(解决网络穿透) │
│ 7. 监听 ontrack 事件,显示对方视频流 │
└──────────────────────────────────────────────────────────┘
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebRTC 核心 API 示例</title>
</head>
<body>
<h1>WebRTC 核心 API 示例</h1>
<div>
<div>
<h3>本地视频</h3>
<video id="localVideo" autoplay muted></video>
</div>
<div>
<h3>远程视频</h3>
<video id="remoteVideo" autoplay></video>
</div>
</div>
<div>
<button id="startButton">开启摄像头</button>
<button id="callButton" disabled>发起通话</button>
<button id="hangupButton" disabled>挂断</button>
</div>
<div>
<h3>实时聊天</h3>
<div id="chatMessages"></div>
<div>
<input type="text" id="chatInput" placeholder="输入消息...">
<button id="sendButton" disabled>发送</button>
</div>
</div>
<div>
<h3>日志</h3>
<div id="logMessages"></div>
</div>
<script>
// DOM 元素
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
const startButton = document.getElementById('startButton');
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
const sendButton = document.getElementById('sendButton');
const chatInput = document.getElementById('chatInput');
const chatMessages = document.getElementById('chatMessages');
const logMessages = document.getElementById('logMessages');
// 状态变量
let localStream;
let peerConnection;
let dataChannel;
let isCaller = false; // 是否为呼叫方
// 日志函数
function log(message) {
const logEntry = document.createElement('div');
logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logMessages.appendChild(logEntry);
logMessages.scrollTop = logMessages.scrollHeight;
}
// 聊天消息函数
function addChatMessage(message, isLocal = false) {
const messageDiv = document.createElement('div');
messageDiv.textContent = isLocal ? `我: ${message}` : `对方: ${message}`;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// 1. 开启摄像头
startButton.addEventListener('click', async () => {
try {
log('请求摄像头和麦克风权限...');
localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
// 显示本地视频
localVideo.srcObject = localStream;
log('摄像头和麦克风已开启');
// 更新按钮状态
startButton.disabled = true;
callButton.disabled = false;
} catch (error) {
log(`获取媒体流失败: ${error.message}`);
alert(`获取媒体流失败: ${error.message}`);
}
});
// 2. 发起通话
callButton.addEventListener('click', () => {
isCaller = true;
log('准备发起通话...');
createPeerConnection();
// 呼叫方创建数据通道
createDataChannel();
// 呼叫方创建 Offer
createOffer();
// 更新按钮状态
callButton.disabled = true;
hangupButton.disabled = false;
sendButton.disabled = false;
});
// 3. 创建 RTCPeerConnection
function createPeerConnection() {
try {
// 配置 STUN/TURN 服务器
const configuration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' }
]
};
peerConnection = new RTCPeerConnection(configuration);
log('RTCPeerConnection 创建成功');
// 添加本地媒体流
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
log('已添加本地媒体流');
// 监听远程媒体流
peerConnection.ontrack = (event) => {
log('接收到远程媒体流');
remoteVideo.srcObject = event.streams[0];
};
// 监听 ICE 候选者
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
log('发送 ICE 候选者到对方');
// 实际项目中通过信令服务器发送
sendToSignalingServer({
type: 'ice-candidate',
candidate: event.candidate
});
}
};
// 监听连接状态
peerConnection.onconnectionstatechange = () => {
log(`连接状态: ${peerConnection.connectionState}`);
if (peerConnection.connectionState === 'disconnected' ||
peerConnection.connectionState === 'failed') {
log('连接断开,尝试重新连接...');
hangup();
}
};
// 接收方监听数据通道
peerConnection.ondatachannel = (event) => {
log('接收到数据通道');
dataChannel = event.channel;
setupDataChannel();
};
} catch (error) {
log(`创建 RTCPeerConnection 失败: ${error.message}`);
}
}
// 4. 创建 Offer
async function createOffer() {
try {
log('创建 Offer...');
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
log('已设置本地 Offer');
// 通过信令服务器发送 Offer
sendToSignalingServer({
type: 'offer',
sdp: offer.sdp
});
} catch (error) {
log(`创建 Offer 失败: ${error.message}`);
}
}
// 5. 创建数据通道
function createDataChannel() {
try {
log('创建数据通道...');
dataChannel = peerConnection.createDataChannel('chat', {
ordered: true, // 消息按顺序到达
maxRetransmits: 3 // 最大重传次数
});
setupDataChannel();
} catch (error) {
log(`创建数据通道失败: ${error.message}`);
}
}
// 6. 设置数据通道事件监听
function setupDataChannel() {
dataChannel.onopen = () => {
log('数据通道已打开');
sendButton.disabled = false;
};
dataChannel.onclose = () => {
log('数据通道已关闭');
sendButton.disabled = true;
};
dataChannel.onerror = (error) => {
log(`数据通道错误: ${error.message}`);
};
dataChannel.onmessage = (event) => {
log(`收到数据: ${event.data}`);
addChatMessage(event.data);
};
}
// 7. 处理来自信令服务器的消息(模拟)
function handleSignalingMessage(message) {
switch (message.type) {
case 'offer':
log('收到 Offer');
// 如果是接收方,创建 RTCPeerConnection
if (!peerConnection) {
createPeerConnection();
// 更新按钮状态
callButton.disabled = true;
hangupButton.disabled = false;
}
// 设置远程 Offer
peerConnection.setRemoteDescription(new RTCSessionDescription(message))
.then(() => {
log('已设置远程 Offer');
// 创建 Answer
return peerConnection.createAnswer();
})
.then(answer => {
log('创建 Answer...');
return peerConnection.setLocalDescription(answer);
})
.then(() => {
log('已设置本地 Answer');
// 发送 Answer 到呼叫方
sendToSignalingServer({
type: 'answer',
sdp: peerConnection.localDescription.sdp
});
})
.catch(error => {
log(`处理 Offer 失败: ${error.message}`);
});
break;
case 'answer':
log('收到 Answer');
// 设置远程 Answer
peerConnection.setRemoteDescription(new RTCSessionDescription(message))
.then(() => {
log('已设置远程 Answer,连接已建立');
})
.catch(error => {
log(`处理 Answer 失败: ${error.message}`);
});
break;
case 'ice-candidate':
log('收到 ICE 候选者');
// 添加 ICE 候选者
peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate))
.catch(error => {
log(`添加 ICE 候选者失败: ${error.message}`);
});
break;
}
}
// 8. 模拟信令服务器通信(实际需替换为WebSocket)
function sendToSignalingServer(message) {
// 实际项目中,这里应该使用WebSocket连接到信令服务器
// 此处仅为演示,模拟消息发送
log(`发送到信令服务器: ${message.type}`);
// 模拟接收方处理(实际需服务器转发)
setTimeout(() => {
if (window.receiver && window.receiver.handleSignalingMessage) {
window.receiver.handleSignalingMessage(message);
} else {
log('提示: 实际项目中应通过信令服务器转发消息');
}
}, 500);
}
// 9. 发送聊天消息
sendButton.addEventListener('click', () => {
const message = chatInput.value.trim();
if (message && dataChannel && dataChannel.readyState === 'open') {
dataChannel.send(message);
addChatMessage(message, true);
chatInput.value = '';
}
});
// 10. 挂断通话
hangupButton.addEventListener('click', hangup);
function hangup() {
log('正在挂断通话...');
// 关闭数据通道
if (dataChannel && dataChannel.readyState !== 'closed') {
dataChannel.close();
}
// 关闭 RTCPeerConnection
if (peerConnection) {
peerConnection.close();
peerConnection = null;
}
// 停止媒体流
if (localStream) {
localStream.getTracks().forEach(track => track.stop());
localStream = null;
localVideo.srcObject = null;
remoteVideo.srcObject = null;
}
// 更新按钮状态
callButton.disabled = false;
hangupButton.disabled = true;
sendButton.disabled = true;
chatInput.value = '';
log('通话已挂断');
}
// 11. 监听键盘回车发送消息
chatInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendButton.click();
}
});
// 12. 模拟接收方(仅演示,实际需服务器支持)
window.receiver = {
handleSignalingMessage: handleSignalingMessage
};
</script>
</body>
</html>
3web socket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,非常适合实时应用(如聊天、游戏、数据推送等)。下面是 JavaScript 中使用 WebSocket 的基础模板:
1. 基本连接模板
// 创建 WebSocket 连接
const socket = new WebSocket('ws://example.com/socket');
// 监听连接事件
socket.onopen = () => {
console.log('WebSocket 连接已打开');
// 发送消息
socket.send('Hello, server!');
};
// 监听消息事件
socket.onmessage = (event) => {
console.log('收到消息:', event.data);
};
// 监听错误事件
socket.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
// 监听关闭事件
socket.onclose = (event) => {
console.log('WebSocket 连接已关闭', event);
// 尝试重连(可选)
if (event.code !== 1000) { // 非正常关闭
reconnect();
}
};
// 重连函数(可选)
function reconnect() {
setTimeout(() => {
console.log('尝试重新连接...');
// 递归调用重连函数
// 实际应用中可能需要指数退避策略
}, 3000);
}
// 关闭连接
function closeConnection() {
socket.close();
}
2. JSON 消息处理模板
const socket = new WebSocket('ws://example.com/json-socket');
// 发送 JSON 消息
function sendJsonMessage(type, data) {
const message = JSON.stringify({ type, data });
socket.send(message);
}
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 根据消息类型处理
switch (data.type) {
case 'chat':
handleChatMessage(data.content);
break;
case 'notification':
showNotification(data.message);
break;
default:
console.log('未知消息类型:', data);
}
} catch (error) {
console.error('解析 JSON 消息失败:', error);
}
};
解析:
WebSocket JSON 消息发送函数解析
1. 函数结构与参数
function sendJsonMessage(type, data) {
const message = JSON.stringify({ type, data });
socket.send(message);
}
- 参数:
type
:消息类型(如'chat'
、'notification'
)data
:消息内容(可以是任何可序列化的数据)
- 返回值:无(异步发送)
2. 消息构建过程
创建对象:
{ type, data }
这是 ES6 的简写语法,等同于:
{ type: type, data: data }
序列化为 JSON 字符串:
JSON.stringify({ type, data })
例如,当调用
sendJsonMessage('chat', 'Hello')
时,生成的字符串是:{"type":"chat","data":"Hello"}
通过 WebSocket 发送:
socket.send(message);
这里的
socket
是已连接的 WebSocket 实例。
3. 完整使用示例
// 初始化 WebSocket
const socket = new WebSocket('ws://example.com/socket');
// 封装的 JSON 消息发送函数
function sendJsonMessage(type, data) {
const message = JSON.stringify({ type, data });
/*用于将 JavaScript 对象或值转换为 JSON 格式的字符串。这个过程称为 序列化(Serialization)*/
socket.send(message);
}
// 连接建立后发送消息
socket.onopen = () => {
// 发送聊天消息
sendJsonMessage('chat', {
content: 'Hello, everyone!',
userId: 12345
});
// 发送命令
sendJsonMessage('command', {
action: 'join_room',
roomId: 'general'
});
};
// 接收并解析消息
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 根据 type 处理不同类型的消息
switch (data.type) {
case 'chat':
console.log('收到聊天消息:', data.data.content);
break;
case 'notification':
console.log('收到通知:', data.data.message);
break;
default:
console.log('未知消息类型:', data);
}
} catch (error) {
console.error('解析消息失败:', error);
}
};
3. 心跳检测模板
const socket = new WebSocket('ws://example.com/socket');
let heartbeatInterval;
const HEARTBEAT_INTERVAL = 30000; // 30秒
// 发送心跳
function sendHeartbeat() {
if (socket.readyState === WebSocket.OPEN) {
socket.send('ping');
}
}
// 启动心跳
function startHeartbeat() {
heartbeatInterval = setInterval(sendHeartbeat, HEARTBEAT_INTERVAL);
}
// 停止心跳
function stopHeartbeat() {
clearInterval(heartbeatInterval);
}
socket.onopen = () => {
console.log('连接已打开,启动心跳');
startHeartbeat();
};
socket.onclose = () => {
console.log('连接已关闭,停止心跳');
stopHeartbeat();
};
socket.onmessage = (event) => {
if (event.data === 'pong') {
// 收到服务器响应的心跳
return;
}
// 处理其他消息
};
4. 事件驱动的 WebSocket 封装
class WebSocketClient {
constructor(url) {
this.url = url;
this.socket = null;
this.eventHandlers = {};
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 10;
this.reconnectInterval = 3000; // 3秒
}
// 连接 WebSocket
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
this.reconnectAttempts = 0;
this.emit('open');
};
this.socket.onmessage = (event) => {
this.emit('message', event.data);
};
this.socket.onerror = (error) => {
this.emit('error', error);
};
this.socket.onclose = (event) => {
this.emit('close', event);
this.reconnect();
};
}
// 重连逻辑
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, this.reconnectInterval);
} else {
this.emit('reconnect_failed');
}
}
// 发送消息
send(data) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(data);
} else {
this.emit('send_failed', data);
}
}
// 关闭连接
close() {
if (this.socket) {
this.socket.close();
}
}
// 事件监听
on(event, handler) {
if (!this.eventHandlers[event]) {
this.eventHandlers[event] = [];
}
this.eventHandlers[event].push(handler);
}
// 事件触发
emit(event, ...args) {
if (this.eventHandlers[event]) {
this.eventHandlers[event].forEach(handler => handler(...args));
}
}
}
// 使用示例
const client = new WebSocketClient('ws://example.com/socket');
client.on('open', () => {
console.log('连接已打开');
client.send('Hello!');
});
client.on('message', (data) => {
console.log('收到消息:', data);
});
client.on('error', (error) => {
console.error('WebSocket 错误:', error);
});
client.on('close', (event) => {
console.log('连接已关闭', event);
});
client.connect();