当前位置: 首页 > news >正文

前端怎么测网速?

文章目录

  • 原理解析
  • 代码实操

如果只是想单纯地测试本机的网络速度,那么可以在线测试 https://www.speedtest.cn/,或者很多桌面工具也都有提供测速的工具。

本文所谓的测网速,是测试你的网站服务器的网速,而不是你本机的网速~

原理解析

测网速的原理其实很简单:

  1. 在服务器上准备一些文件供下载用;
  2. 多次测试下载这些文件用了多长时间;
  3. 然后用时间和下载的文件大小计算一下,即可得出平均网速;

示例如下:
在这里插入图片描述

代码实操

了解了原理,那么我们就可以写一个测网速的组件了。

我们要准备两点:

  • 在服务器上准备一个文件,并记录文件大小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">
          <div
            v-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 = true
  controller.value = new AbortController()

  try {
    for (let i = 0; i < totalTests; i++) {
      await measureSpeed()
      testCount.value = i + 1
      progress.value = ((i + 1) / totalTests) * 100
    }
  } finally {
    isTesting.value = false
  }
}

// 核心测速方法
const measureSpeed = async () => {
  let loadedBytes = 0
  const 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) break
      loadedBytes += value?.length || 0
    }

    const duration = (performance.now() - startTime) / 1000 // 秒
    const speedMbps = (loadedBytes * 8) / duration / 1e6 // 转换为Mbps
    speedHistory.value.push(speedMbps)
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error('测速失败:', error)
    }
  }
}

// 取消测试
const cancelTest = () => {
  controller.value?.abort()
  isTesting.value = false
}

// 重置状态
const resetState = () => {
  progress.value = 0
  speedHistory.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>

相关文章:

  • Python数据类型进阶——详解
  • 麒麟v10 ARM64架构系统升级mysql数据库从mysql-5.7.27到mysql-8.4.4图文教程
  • (vue)elementUi中el-upload上传附件之后 点击附件可下载
  • 拥抱AI未来:Hugging Face平台使用指南与实战技巧
  • 八叉树地图的原理与实现
  • 360安全软件拦截鼠标键盘模拟操作的解决方法
  • 青年公寓服务平台的设计与实现(代码+数据库+LW)
  • Linux下用多进程在GPU上跑Pytorch模型问题
  • 大模型在原发性急性闭角型青光眼预测及治疗方案制定中的应用研究报告
  • 字母金字塔
  • 深度学习核心技术深度解析
  • 【HarmonyOS NEXT】实现文字环绕动态文本效果
  • LeetCode455☞分发饼干
  • 第1关:整数对
  • 网络空间安全(28)风险评估实施
  • 《C#上位机开发从门外到门内》2-7:网络通信(TCP/IP、UDP)
  • Java学习路线
  • QT—环境监控系统
  • 直线导轨在数控机床中的使用方法
  • 【原创】springboot+vue校园新冠疫情统计管理系统设计与实现
  • 东莞营销商城网站建设/开发app需要多少资金
  • 网站制作国际连锁/宁波网络推广方式
  • 政治建设求是网/上海优化seo
  • 做封面的软件ps下载网站/seo营销网站的设计标准
  • 百度网盟网站有哪些/淘宝seo具体优化方法
  • 班组安全建设 网站/优化最狠的手机优化软件