优化网站性能网站备案管理办法
- 作者: 五速梦信息网
- 时间: 2026年04月20日 06:55
当前位置: 首页 > news >正文
优化网站性能,网站备案管理办法,wordpress电影网盘,旅游电子商务网站建设中最重要的环节和内容是什么1 基本对象 1.1 DOCUMENT 定义#xff1a;浏览器内置的全局对象#xff08;window.document#xff09;#xff0c;提供访问和操作 HTML 文档的接口。 核心功能#xff1a; 查找和选择 HTML 元素#xff08;如 div、input#xff09;。修改元素的内容、属性和样式。创…1 基本对象 1.1 DOCUMENT 定义浏览器内置的全局对象window.document提供访问和操作 HTML 文档的接口。 核心功能 查找和选择 HTML 元素如 div、input。修改元素的内容、属性和样式。创建新元素并添加到文档中。监听用户事件如点击、滚动
- 查找和选择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 对象的属性会根据事件类型动态变化以下是常见场景 事件类型特有属性示例场景点击事件clickevent.clientX, event.clientY计算鼠标点击位置键盘事件keydownevent.key, event.ctrlKey, event.shiftKey判断用户是否按下组合键表单输入inputevent.target.value实时获取输入框内容鼠标移动mousemoveevent.pageX, event.pageY实现拖拽效果、跟随鼠标的元素窗口滚动scrollevent.target.scrollTop监听页面滚动位置实现导航栏动态效果音视频事件play/pauseevent.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 基本概念 WebRTCWeb 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绑定媒体流摄像头、麦克风和数据通道 - 关键用法极简流程 配置与创建连接 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 langzh-CN headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleWebRTC 核心 API 示例/title /head bodyh1WebRTC 核心 API 示例/h1divdivh3本地视频/h3video idlocalVideo autoplay muted/video/divdivh3远程视频/h3video idremoteVideo autoplay/video/div/divdivbutton idstartButton开启摄像头/buttonbutton idcallButton disabled发起通话/buttonbutton idhangupButton disabled挂断/button/divdivh3实时聊天/h3div idchatMessages/divdivinput typetext idchatInput placeholder输入消息…button idsendButton disabled发送/button/div/divdivh3日志/h3div idlogMessages/div/divscript// 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();// 呼叫方创建 OffercreateOffer();// 更新按钮状态callButton.disabled true;hangupButton.disabled false;sendButton.disabled false;});// 3. 创建 RTCPeerConnectionfunction 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. 创建 Offerasync function createOffer() {try {log(创建 Offer…);const offer await peerConnection.createOffer();await peerConnection.setLocalDescription(offer);log(已设置本地 Offer);// 通过信令服务器发送 OffersendToSignalingServer({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);// 如果是接收方创建 RTCPeerConnectionif (!peerConnection) {createPeerConnection();// 更新按钮状态callButton.disabled true;hangupButton.disabled false;}// 设置远程 OfferpeerConnection.setRemoteDescription(new RTCSessionDescription(message)).then(() {log(已设置远程 Offer);// 创建 Answerreturn 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);// 设置远程 AnswerpeerConnection.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. 模拟信令服务器通信实际需替换为WebSocketfunction 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();}// 关闭 RTCPeerConnectionif (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 /html3web socket WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议非常适合实时应用如聊天、游戏、数据推送等。下面是 JavaScript 中使用 WebSocket 的基础模板
- 基本连接模板 // 创建 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 消息发送函数解析
- 函数结构与参数 function sendJsonMessage(type, data) {const message JSON.stringify({ type, data });socket.send(message); }参数 type消息类型如 chat、notificationdata消息内容可以是任何可序列化的数据 返回值无异步发送
- 消息构建过程 创建对象 { 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 实例。
- 完整使用示例 // 初始化 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秒}// 连接 WebSocketconnect() {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();
- 上一篇: 优化网站推广排名销售管理软件app
- 下一篇: 优惠券的网站怎么做浙江建设集团
相关文章
-
优化网站推广排名销售管理软件app
优化网站推广排名销售管理软件app
- 技术栈
- 2026年04月20日
-
优化网站是什么意思为公司做网站
优化网站是什么意思为公司做网站
- 技术栈
- 2026年04月20日
-
优化网站排名推广手机网站怎么做淘宝客
优化网站排名推广手机网站怎么做淘宝客
- 技术栈
- 2026年04月20日
-
优惠券的网站怎么做浙江建设集团
优惠券的网站怎么做浙江建设集团
- 技术栈
- 2026年04月20日
-
优惠券网站要怎么做的替别人做网站
优惠券网站要怎么做的替别人做网站
- 技术栈
- 2026年04月20日
-
优惠券网站怎么做代理江苏省实训基地建设网站
优惠券网站怎么做代理江苏省实训基地建设网站
- 技术栈
- 2026年04月20日
