2345网站登录新闻网站排行榜
文章目录
- 原理解析
- 代码实操
如果只是想单纯地测试本机的网络速度,那么可以在线测试 https://www.speedtest.cn/,或者很多桌面工具也都有提供测速的工具。
本文所谓的测网速,是测试你的网站服务器的网速,而不是你本机的网速~
原理解析
测网速的原理其实很简单:
- 在服务器上准备一些文件供下载用;
- 多次测试下载这些文件用了多长时间;
- 然后用时间和下载的文件大小计算一下,即可得出平均网速;
示例如下:
代码实操
了解了原理,那么我们就可以写一个测网速的组件了。
我们要准备两点:
- 在服务器上准备一个文件,并记录文件大小size(一般几M大小即可)
- 实现代码
我准备的文件配置如下
const TEST_FILE = { url:'https://www.xxx.com/20240412/10/34/4/0dedaa5dbc6c.mp4?v=1',size: 2680833 // 约等于2.57M大小
}
效果图如下:
整个组件的代码如下,因为是8k大屏弹框组件,所以文字大小、长宽等都很大,这里不做整理了,仅提供逻辑思路。
注意点:测试下载的文件,一定要加唯一标识的后缀,否则后面可能会从浏览器缓存取,导致测速不准确;
牛马上班抽空写文章、没时间整理了,请见谅。还有冗余的代码,都是从其他同事的组件里复制的
<template><div class="layer" v-if="isShowLockBox == 1"><div class="dialog" v-if="isShowLockBox == 1"><div class="title">测试网速</div><div class="fl-right" @click="isShowLockBox = 0;averageSpeed = 0"><img class="fl-img" src="@/assets/images/home/crossOff.png"/><img class="fl-img-active" src="@/assets/images/home/crossOffActive.png"/></div><div class="speed-test"><div class="controls"><button @click="startTest" :disabled="isTesting" style="font-size: 70px;">{{ isTesting ? '测试中...' : '开始测速' }}</button><button @click="cancelTest" v-if="isTesting" style="font-size: 70px;">取消</button></div><div class="progress" v-if="isTesting"><div class="progress-bar" :style="{ width: progress + '%' }"></div></div><div class="results"><div class="speed-display"><span class="value">{{ averageSpeed }}</span><span class="unit">M/s</span></div></div><div class="chart" v-if="speedHistory.length > 0"><divv-for="(speed, index) in speedHistory":key="index"class="chart-bar":style="{ height: Math.min(speed * 5, 100) + '%' }"></div></div></div></div></div><div class="lock-btns" title="测网速"><img :src="$loadLocalImg('lock/netSpeed.png')" class="lock-icon" alt="测试网速" @click="handleLockOpen"></div>
</template><script setup>
import { ref, computed } from 'vue'// 是否显示遮罩层的输入框,默认不显示
const isShowLockBox = ref(0)
/** 解锁,仅显示输入框 */
const handleLockOpen = () => {isShowLockBox.value = 1
}// 测试文件配置
const TEST_FILE = {url: 'https://www.xxx.com/group1/default/20240412/10/34/4/0da5dbc6c.mp4?v=1',size: 2680833 // 500KB
}const isTesting = ref(false)
const progress = ref(0)
const speedHistory = ref([])
const controller = ref(null)
const testCount = ref(0)
const totalTests = 10// 计算平均速度
const averageSpeed = computed(() => {if (speedHistory.value.length === 0) return '0.00'const sum = speedHistory.value.reduce((a, b) => a + b, 0)return (sum / speedHistory.value.length).toFixed(2)
})// 开始测试
const startTest = async () => {resetState()isTesting.value = truecontroller.value = new AbortController()try {for (let i = 0; i < totalTests; i++) {await measureSpeed()testCount.value = i + 1progress.value = ((i + 1) / totalTests) * 100}} finally {isTesting.value = false}
}// 核心测速方法
const measureSpeed = async () => {let loadedBytes = 0const startTime = performance.now()const randomParam = Math.random()const urlWithRandom = `${TEST_FILE.url}&r=${randomParam}`try {const response = await fetch(urlWithRandom, {signal: controller.value.signal})const reader = response.body.getReader()while (true) {const { done, value } = await reader.read()if (done) breakloadedBytes += value?.length || 0}const duration = (performance.now() - startTime) / 1000 // 秒const speedMbps = (loadedBytes * 8) / duration / 1e6 // 转换为MbpsspeedHistory.value.push(speedMbps)} catch (error) {if (error.name !== 'AbortError') {console.error('测速失败:', error)}}
}// 取消测试
const cancelTest = () => {controller.value?.abort()isTesting.value = false
}// 重置状态
const resetState = () => {progress.value = 0speedHistory.value = []testCount.value = 0
}
</script><style lang="scss" scoped>
.speed-test {width: 1200px;height: 640px;margin: 2rem auto;padding: 20px;background: rgba(11, 20, 39, 0.8);border-radius: 12px;
}.controls {margin-bottom: 1rem;
}button {padding: 8px 16px;margin-right: 10px;background: #007bff;color: white;border: none;border-radius: 4px;cursor: pointer;
}button:disabled {background: #6c757d;
}.progress {height: 20px;background: #ddd;border-radius: 5px;overflow: hidden;
}.progress-bar {height: 100%;background: #28a745;transition: width 0.3s ease;
}.speed-display {text-align: center;margin: 2rem 0;
}.value {font-size: 8rem;font-weight: bold;color: #fff;
}.unit {font-size: 4.2rem;color: #fff;
}.chart {display: flex;height: 150px;align-items: flex-end;gap: 2px;margin-top: 1rem;
}.chart-bar {flex: 1;background: #007bff;transition: height 0.5s ease;
}.layer {position: fixed;left: 0;right: 0;top: 0;bottom: 0;z-index: 9999;background-color: rgba(0, 0, 0, 0.05);display: flex;justify-content: center;align-items: center;pointer-events: auto;
}
.dialog {position: relative;width: 1372px;border-radius: 20px;text-align: center;color: #ffffff;background-color: #020A1A;.title {font-size: 80px;background: #134392;height: 150px;line-height: 150px;}.input-box {width: 1202px;height: 117px;margin: 123px auto 0;:deep(.el-input__wrapper) {padding: 0;border-radius: 20px;overflow: hidden;}:deep(.el-input__inner) {height: 118px;line-height: 118px;font-size: 64px;border: 4px solid #ffffff;border-radius: 20px;padding-left: 40px;background-color: #000000;color: #ffffff;& > ::placeholder {color: rgba(255, 255, 255, 0.5)}}}.btns {display: flex;flex-direction: row;justify-content: space-between;width: 1202px;margin: 200px auto 85px;.lock-btn {width: 518px;height: 113px;line-height: 113px;border-radius: 20px;text-align: center;background: rgba(255,255,255,0.2);font-size: 44px;color: #ffffff;cursor: pointer;}& > div:last-child {background-color: #0067E6;}}
}.lock-btns {position: fixed;bottom: 25px;right: 1400px;z-index: 10000;
}
.lock-icon {width: 130px;cursor:pointer;pointer-events: auto;
}.fl-right {pointer-events: auto;cursor: pointer;position: absolute;top: 40px;right: 40px;&:hover {img {&.fl-img {display: none;}&.fl-img-active {display: initial;}}}img {width: 57px;height: 57px;&.fl-img-active {display: none;}}
}
</style>