如何实现网页加载进度条?

前言: 当用户打开你的页面,面对一片空白等待加载时,体验是焦虑的。一个流畅的进度条能告诉用户"正在加载,请稍候",极大提升感知速度。本文从进度获取、渲染方式、页面加载、框架集成四个维度,给你一套完整的实现方案。


目录


一、背景:为什么需要加载进度条?

arduino 复制代码
没有进度条的用户心理:
  打开页面 → 空白... → 还在空白... → "这网站是不是挂了?" → 离开 ❌

有进度条的用户心理:
  打开页面 → 看到进度条在走 → "哦,在加载" → 继续等待 → 加载完成 ✅

核心价值:

  • 降低用户焦虑感 --- 可视化反馈让等待变得可预期
  • 提升感知性能 --- 同样的加载时间,有进度条感觉更快
  • 传递专业感 --- 细节体现产品品质

二、核心问题拆解

实现一个加载进度条,本质上要解决两个问题:

2.1 怎么拿到进度?

方法 兼容性 适用场景 精度
fetchProgressEvent 现代浏览器 文件上传/下载 ⭐⭐⭐⭐⭐ 高精度
XMLHttpRequestonprogress 全兼容 旧项目/AJAX ⭐⭐⭐⭐⭐ 高精度
Performance API IE10+ 页面整体加载 ⭐⭐⭐ 中等精度
路由钩子/拦截器 框架内 SPA 路由切换 ⭐⭐ 模拟进度
模拟递增 全兼容 无法获取真实进度时 ⭐ 纯视觉

2.2 怎么绘制进度?

方式 特点 适用场景
DOM + CSS 简单直接,样式灵活 大多数场景首选
SVG 矢量无损,支持复杂动画 需要圆环/特殊形状时
Canvas 高性能,适合复杂图形 大量动画/游戏场景

三、方案一:页面级加载进度条

监听浏览器的页面加载事件,展示从导航开始到页面完全就绪的进度。

3.1 基于 Performance API 的真实进度

js 复制代码
class PageLoadingBar {
  constructor(options = {}) {
    this.container = options.container || document.body;
    this.color = options.color || '#42b883';
    this.height = options.height || '3px';
    this.position = options.position || 'top'; // top 或 bottom
    this._progress = 0;
    this._init();
  }

  _init() {
    // 创建进度条 DOM
    this.bar = document.createElement('div');
    Object.assign(this.bar.style, {
      position: 'fixed',
      left: '0',
      [this.position]: '0',
      width: '0%',
      height: this.height,
      backgroundColor: this.color,
      transition: 'width 0.3s ease-out',
      zIndex: '99999',
      boxShadow: `0 0 10px ${this.color}40`,
    });
    this.container.appendChild(this.bar);
  }

  setProgress(value) {
    this._progress = Math.min(100, Math.max(0, value));
    this.bar.style.width = `${this._progress}%`;
  }

  start() {
    this.setProgress(0);
    this._startMonitoring();
  }

  finish() {
    this.setProgress(100);
    // 动画结束后移除
    setTimeout(() => {
      if (this.bar.parentNode) {
        this.bar.style.opacity = '0';
        setTimeout(() => this.bar.remove(), 300);
      }
    }, 300);
  }

  _startMonitoring() {
    // 使用 Performance API 追踪页面加载阶段
    const updateProgress = () => {
      const perf = performance.getEntriesByType('navigation')[0];
      if (!perf) return;

      // 根据各事件估算进度
      const stages = [
        { event: 'responseStart',   progress: 30 },  // 收到首字节
        { event: 'domInteractive',  progress: 60 },  // DOM 可交互
        { event: 'domContentLoadedEventStart', progress: 80 }, // DOM 解析完成
        { event: 'loadEventEnd',    progress: 100 }, // 完全加载
      ];

      for (const stage of stages) {
        if (perf[stage.event] > 0) {
          this.setProgress(Math.max(this._progress, stage.progress));
        }
      }
    };

    // 定期检查进度
    this._timer = setInterval(updateProgress, 50);

    // 同时用模拟递增兜底(防止某些阶段无法检测)
    this._simulate();

    // 监听 load 事件
    window.addEventListener('load', () => {
      clearInterval(this._timer);
      this.finish();
    }, { once: true });
  }

  _simulate() {
    // 模拟渐进式增长,让进度条看起来一直在动
    const simulate = () => {
      if (this._progress >= 90) return; // 90% 后停止模拟,等真实事件

      let increment = 0;
      if (this._progress < 30) increment = Math.random() * 8 + 2;     // 前段快
      else if (this._progress < 60) increment = Math.random() * 5 + 1; // 中段匀速
      else increment = Math.random() * 2 + 0.5;                        // 后段慢

      this.setProgress(this._progress + increment);

      this._simTimer = setTimeout(simulate, 200 + Math.random() * 300);
    };
    simulate();
  }

  destroy() {
    clearInterval(this._timer);
    clearTimeout(this._simTimer);
    if (this.bar.parentNode) this.bar.remove();
  }
}

// 使用
const loader = new PageLoadingBar({ color: '#409eff' });
loader.start();
// 页面加载完成后自动调用 finish()

3.2 极简版:NProgress 风格的顶部进度条

html 复制代码
<!-- 直接复制粘贴即可使用 -->
<style>
  .nprogress-bar {
    position: fixed;
    top: 0; left: 0;
    width: 0%; height: 3px;
    background: #42b883;
    z-index: 99999;
    transition: width 0.2s ease-out;
  }
  .nprogress-bar.complete {
    opacity: 0;
    transition: width 0.3s linear, opacity 0.3s 0.2s ease-out;
  }
</style>

<div class="nprogress-bar" id="top-loader"></div>

<script>
  const bar = document.getElementById('top-loader');
  let progress = 0;

  function startLoader() {
    progress = 0;
    bar.style.width = '0%';
    bar.classList.remove('complete');
    requestAnimationFrame(animate);
  }

  function animate() {
    if (progress < 90) {
      // 非线性增速:越往后越慢
      progress += (90 - progress) * 0.03 + Math.random() * 2;
      bar.style.width = Math.min(progress, 90) + '%';
      requestAnimationFrame(animate);
    }
  }

  function doneLoader() {
    bar.style.width = '100%';
    bar.classList.add('complete');
  }

  // 使用
  startLoader();
  window.addEventListener('load', doneLoader);
</script>

四、方案二:数据请求进度追踪

针对 AJAX/Fetch 请求,获取真实的下载/上传进度。

4.1 Fetch API + ReadableStream(现代方案)

js 复制代码
async function fetchWithProgress(url, options = {}) {
  const { onProgress } = options;

  const response = await fetch(url, options);

  if (!onProgress || !response.body) return response;

  const contentLength = response.headers.get('content-length');
  const total = parseInt(contentLength, 10) || 0;
  let loaded = 0;

  const reader = response.body.getReader();
  const stream = new ReadableStream({
    async start(controller) {
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          onProgress({ loaded: total || loaded, total, percent: 100 });
          controller.close();
          break;
        }
        loaded += value.length;
        const percent = total ? Math.round((loaded / total) * 100) : null;
        onProgress({ loaded, total, percent });
        controller.enqueue(value);
      }
    },
  });

  return new Response(stream, {
    headers: response.headers,
    status: response.status,
    statusText: response.statusText,
  });
}

// 使用示例
fetchWithProgress('/api/large-data', {
  onProgress: ({ loaded, total, percent }) => {
    console.log(`已下载 ${loaded}/${total} 字节 (${percent}%)`);
    updateProgressBar(percent); // 更新你的进度条 UI
  },
}).then(res => res.json()).then(data => {
  console.log('数据加载完成:', data);
});

4.2 XMLHttpRequest 进度追踪(经典方案)

js 复制代码
function xhrWithProgress(url, callbacks = {}) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);

    // ✅ 下载进度
    xhr.onprogress = (e) => {
      if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        callbacks.onDownloadProgress?.({
          loaded: e.loaded,
          total: e.total,
          percent,
        });
      }
    };

    // ✅ 上传进度
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        callbacks.onUploadProgress?.({
          loaded: e.loaded,
          total: e.total,
          percent,
        });
      }
    };

    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`HTTP ${xhr.status}`));
      }
    };

    xhr.onerror = () => reject(new Error('Network error'));
    xhr.send();
  });
}

// 使用示例:带进度的大文件上传
async function uploadFile(file) {
  const formData = new FormData();
  formData.append('file', file);

  try {
    const result = await xhrWithProgress('/api/upload', {
      onUploadProgress: ({ percent }) => {
        document.getElementById('upload-progress').style.width = percent + '%';
        document.getElementById('upload-text').textContent = `上传中 ${percent}%`;
      },
    });
    console.log('上传成功:', result);
  } catch (err) {
    console.error('上传失败:', err);
  }
}

4.3 Axios 请求拦截器统一处理

js 复制代码
import axios from 'axios';

// 创建实例
const api = axios.create({ baseURL: '/api' });

// 全局进度条状态管理
let pendingRequests = 0;

// 请求拦截器:发起请求时启动/增加进度
api.interceptors.request.use((config) => {
  pendingRequests++;
  if (pendingRequests === 1) {
    globalProgressBar.start(); // 启动全局进度条
  }
  return config;
});

// 响应拦截器:请求完成时减少进度
api.interceptors.response.use(
  (response) => {
    pendingRequests--;
    if (pendingRequests === 0) {
      globalProgressBar.done(); // 所有请求完成
    }
    return response;
  },
  (error) => {
    pendingRequests--;
    if (pendingRequests === 0) {
      globalProgressBar.done();
    }
    return Promise.reject(error);
  }
);

// 单个请求的进度(用于文件上传/下载)
api.post('/upload', formData, {
  onUploadProgress: (progressEvent) => {
    const percent = Math.round(
      (progressEvent.loaded * 100) / progressEvent.total
    );
    uploadProgressRef.current.style.width = `${percent}%`;
  },
});

五、方案三:路由切换进度条(React)

SPA 应用中,路由切换时的白屏同样需要进度条来缓解。最流行的方案是 NProgress 或自研组件。

5.1 React Router + 自定义进度条

jsx 复制代码
// components/RouteLoadingBar.jsx
import { useState, useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';

export default function RouteLoadingBar({ color = '#1890ff', height = 3 }) {
  const location = useLocation();
  const [progress, setProgress] = useState(0);
  const [isVisible, setIsVisible] = useState(false);
  const animFrameRef = useRef(null);

  useEffect(() => {
    // 路由变化时开始
    setIsVisible(true);
    setProgress(0);

    let current = 0;
    const animate = () => {
      if (current < 90) {
        // 缓动函数:先快后慢
        current += (90 - current) * 0.04 + Math.random() * 1.5;
        setProgress(current);
        animFrameRef.current = requestAnimationFrame(animate);
      }
    };
    animFrameRef.current = requestAnimationFrame(animate);

    // 模拟页面加载完成后结束
    const timer = setTimeout(() => {
      cancelAnimationFrame(animFrameRef.current);
      setProgress(100);
      setTimeout(() => setIsVisible(false), 300); // 过渡动画后隐藏
    }, 800);

    return () => {
      clearTimeout(timer);
      cancelAnimationFrame(animFrameRef.current);
    };
  }, [location.pathname]);

  if (!isVisible) return null;

  return (
    <div
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        right: 0,
        height,
        backgroundColor: color,
        transition: 'width 0.2s ease-out, opacity 0.3s ease-out',
        boxShadow: `0 0 8px ${color}50`,
        zIndex: 99999,
        width: `${progress}%`,
      }}
    />
  );
}
jsx 复制代码
// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import RouteLoadingBar from './components/RouteLoadingBar';

function Home() { return <h1>首页</h1>; }
function About() { return <h1>关于我们</h1>; }

export default function App() {
  return (
    <BrowserRouter>
      {/* 放在 Router 内部即可自动工作 */}
      <RouteLoadingBar color="#1677ff" />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

5.2 React Suspense 边界配合

jsx 复制代码
import { Suspense, lazy } from 'react';

// 懒加载组件
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <>
      <RouteLoadingBar />

      <Suspense fallback={
        <div style={{ padding: 40, textAlign: 'center' }}>
          <div className="spinner" /> {/* 可选:加个 spinner */}
          <p>页面加载中...</p>
        </div>
      }>
        <Routes>
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </>
  );
}

六、方案四:路由切换进度条(Vue)

Vue 通过导航守卫来控制路由切换进度条是最优雅的方式。

6.1 Vue Router 导航守卫 + NProgress

js 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

// 配置 NProgress
NProgress.configure({
  easing: 'ease-in-out',   // 动画缓动函数
  speed: 300,              // 动画速度(ms)
  showSpinner: false,      // 是否显示右上角旋转图标
  trickleSpeed: 200,       // 自动增量速率
  minimum: 0.1,           // 最小百分比
});

const routes = [
  { path: '/', component: () => import('@/views/Home.vue') },
  { path: '/about', component: () => import('@/views/About.vue') },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

// ✅ 全局前置守卫:路由跳转前开始
router.beforeEach((to, from, next) => {
  NProgress.start();
  next();
});

// ✅ 全局后置守卫:路由跳转后结束
router.afterEach(() => {
  NProgress.done();
});

export default router;

6.2 纯手写 Vue 版进度条(不依赖第三方)

vue 复制代码
<!-- components/PageLoadingBar.vue -->
<template>
  <transition name="fade">
    <div v-if="loading" class="page-loading-bar">
      <div class="bar" :style="{ width: progress + '%' }"></div>
    </div>
  </transition>
</template>

<script setup>
import { ref, watch } from 'vue';
import { useRouter } from 'vue-router';

const props = defineProps({
  color: { type: String, default: '#42b883' },
  height: { type: String, default: '3px' },
  errorColor: { type: String, default: '#f56c6c' },
});

const router = useRouter();
const loading = ref(false);
const progress = ref(0);
let animFrame = null;

function start() {
  loading.value = true;
  progress.value = 0;
  animate();
}

function animate() {
  if (progress.value >= 90) return;
  progress.value += (90 - progress.value) * 0.04 + Math.random() * 1.5;
  animFrame = requestAnimationFrame(animate);
}

function finish() {
  cancelAnimationFrame(animFrame);
  progress.value = 100;
  setTimeout(() => { loading.value = false; }, 300);
}

// 监听路由变化
router.beforeEach((to, from, next) => {
  start();
  next();
});

router.afterEach(() => {
  // 模拟最小显示时间,避免闪烁
  setTimeout(finish, 300);
});
</script>

<style scoped>
.page-loading-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 99999;
  height: v-bind(height);
}
.bar {
  height: 100%;
  background: v-bind(color);
  transition: width 0.2s ease-out;
  box-shadow: 0 0 8px v-bind(color + '50');
}
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
</style>
vue 复制代码
<!-- App.vue -->
<template>
  <PageLoadingBar color="#409eff" />
  <RouterView />
</template>

<script setup>
import PageLoadingBar from '@/components/PageLoadingBar.vue';
</script>

七、渲染方式对比:DOM vs SVG vs Canvas

7.1 DOM 方式(推荐大多数场景)

css 复制代码
/* 最简单也最常用的方式 */
.loading-bar-dom {
  position: fixed;
  top: 0; left: 0;
  width: 80%; /* 动态修改 */
  height: 4px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  border-radius: 0 2px 2px 0;
  box-shadow: 0 0 12px rgba(102, 126, 234, 0.5);
  transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

优点: CSS 动画 GPU 加速、样式灵活、易于定制

缺点: 复杂形状受限

7.2 SVG 方式(适合圆环/特殊形状)

html 复制代码
<!-- 圆形进度环 -->
<svg width="48" height="48" viewBox="0 0 48 48">
  <!-- 背景圆环 -->
  <circle cx="24" cy="24" r="20"
    fill="none" stroke="#eee" stroke-width="4" />
  <!-- 进度圆环:通过 stroke-dasharray 和 stroke-dashoffset 控制 -->
  <circle id="progress-ring" cx="24" cy="24" r="20"
    fill="none" stroke="#42b883" stroke-width="4"
    stroke-linecap="round"
    stroke-dasharray="125.6"
    stroke-dashoffset="125.6"
    transform="rotate(-90 24 24)"
    style="transition: stroke-dashoffset 0.35s ease" />
</svg>

<script>
function setRingProgress(percent) {
  const ring = document.getElementById('progress-ring');
  const circumference = 2 * Math.PI * 20; // r=20
  const offset = circumference - (percent / 100) * circumference;
  ring.style.strokeDashoffset = offset;
}

setRingProgress(65); // 设置为 65%
</script>

优点: 矢量无损缩放、任意形状、stroke 动画流畅

缺点: 大量 DOM 时性能不如 Canvas

7.3 Canvas 方式(高性能场景)

js 复制代码
class CanvasProgressBar {
  constructor(canvas, options = {}) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.progress = 0;
    this.options = {
      color: options.color || '#409eff',
      bgColor: options.bgColor || '#e8e8e8',
      height: options.height || 4,
      radius: options.radius || 0,
      ...options,
    };
    this._draw();
  }

  setProgress(value) {
    this.progress = Math.min(100, Math.max(0, value));
    this._draw();
  }

  _draw() {
    const { canvas, ctx, progress, options } = this;
    const w = canvas.width;
    const h = options.height;

    ctx.clearRect(0, 0, w, h);

    // 背景
    ctx.fillStyle = options.bgColor;
    ctx.beginPath();
    ctx.roundRect(0, 0, w, h, options.radius);
    ctx.fill();

    // 进度
    const gradient = ctx.createLinearGradient(0, 0, w, 0);
    gradient.addColorStop(0, options.color);
    gradient.addColorStop(1, this._lightenColor(options.color, 30));

    ctx.fillStyle = gradient;
    ctx.beginPath();
    ctx.roundRect(0, 0, (w * progress) / 100, h, options.radius);
    ctx.fill();

    // 高光效果
    ctx.fillStyle = 'rgba(255,255,255,0.3)';
    ctx.beginPath();
    ctx.roundRect(0, 0, (w * progress) / 100, h / 2, [options.radius, options.radius, 0, 0]);
    ctx.fill();
  }

  _lightenColor(hex, percent) {
    const num = parseInt(hex.slice(1), 16);
    const amt = Math.round(2.55 * percent);
    const R = Math.min(255, (num >> 16) + amt);
    const G = Math.min(255, ((num >> 8) & 0x00FF) + amt);
    const B = Math.min(255, (num & 0x0000FF) + amt);
    return `rgb(${R},${G},${B})`;
  }
}

// 使用
const canvas = document.getElementById('progress-canvas');
canvas.width = window.innerWidth;
const bar = new CanvasProgressBar(canvas, { color: '#667eea', height: 3 });

// 动画演示
let p = 0;
const tick = () => {
  p += 0.5;
  bar.setProgress(p);
  if (p < 100) requestAnimationFrame(tick);
};
tick();

优点: 高性能、适合复杂粒子/波纹效果

缺点: 需要手动处理 DPI 缩放、相对 DOM 不够声明式


八、进阶技巧与避坑指南

8.1 进度条的"欺骗艺术"

真相: 大多数网站的进度条都是"假的"。真实进度很难精确获取,所以优秀的进度条都在"演戏"。

shell 复制代码
理想曲线:
  0% ──────────────── 100%(匀速,但实际做不到)

实际策略(欺骗曲线):
  0% ──快速── 30% ──中速── 60% ──慢速── 85% ──卡住── 100%
         ↑            ↑            ↑                  ↑
       给用户希望    保持动力      接近完成的期待      最终冲刺

核心原则:

  1. 永远不要回退 --- 进度只能增加,减少会让用户觉得出问题了
  2. 前期快、后期慢 --- 先给用户信心,后面留余地
  3. 卡在 90%-99% 是正常的 --- 最后阶段往往是最耗时的(渲染 DOM、执行 JS)
  4. 设置超时兜底 --- 如果真的卡住了,强制到 100%

8.2 常见坑

现象 解决方案
进度条闪烁 快速路由切换时反复出现消失 设置最小显示时间(如 300ms)
进度到 99% 就不动了 忘记调用 finish() 在 finally/afterEach 中确保调用
多个请求导致进度条抖动 每个请求都独立控制进度 用计数器统一管理(pendingRequests)
移动端掉帧 CSS transition 太频繁 transform + will-change 触发 GPU 加速
颜色看不清 进度条颜色和页面背景太接近 加阴影或用渐变色增强辨识度

8.3 性能优化要点

css 复制代码
/* ✅ 推荐:GPU 加速的进度条 */
.gpu-bar {
  will-change: width, opacity;   /* 提示浏览器优化 */
  transform: translateZ(0);      /* 触发硬件加速 */
  backface-visibility: hidden;   /* 减少重绘 */
}

/* ❌ 避免:频繁触发布局重排 */
.bad-bar {
  /* 不要在循环中读取 offsetWidth/height 再写入 style */
}
js 复制代码
// ✅ 正确:批量更新 DOM
requestAnimationFrame(() => {
  bar.style.width = newPercent + '%';
});

// ❌ 错误:同步循环更新
for (let i = 0; i <= 100; i++) {
  bar.style.width = i + '%'; // 浪费大量计算,中间状态根本看不到
}

九、总结与选型建议

9.1 场景 × 方案对照表

场景 推荐方案 关键技术
传统多页应用 Performance API + DOM 进度条 performance.getEntriesByType('navigation')
React SPA 路由切换 useLocation + 自定义组件 useEffect 监听 pathname 变化
Vue SPA 路由切换 Router 导航守卫 + NProgress beforeEach / afterEach
大文件上传/下载 XHR onprogress 或 Fetch Stream ReadableStream + onUploadProgress
多个并发 API 请求 Axios 拦截器 + 计数器 interceptors + pendingRequests
需要炫酷效果 Canvas / SVG 渐变、圆环、粒子特效
不想引入依赖 纯原生实现(本文所有代码) 零依赖,复制即用

9.2 开源库推荐

特点 安装
NProgress jQuery 时代的老牌方案,极简 npm install nprogress
react-top-loading-bar React 专用,轻量 npm install react-top-loading-bar
@tanstack/vue-query Vue 数据请求自带 loading 状态 npm install @tanstack/vue-query
angular-progressbar Angular 生态 npm install angular-progressbar

9.3 一句话总结

好的进度条不是精确的报告器,而是耐心的安抚师。 用户不需要知道确切的百分比,他们只需要知道------"系统还在工作,稍等就好"。


如果你觉得这篇文章有帮助,欢迎点赞收藏!有问题欢迎评论区讨论 🎉

相关推荐
小孔龙1 小时前
Android GPU 渲染管线:一帧画面如何走上屏幕
android·性能优化·gpu
xx24062 小时前
前端性能优化笔记
前端·笔记·性能优化
乐启国际旅行社有限公司2 小时前
Java线程池实战:文旅系统批量任务性能优化(团期生成/数据导出)
java·开发语言·性能优化
做前端的娜娜子1 天前
大文件上传
掘金·金石计划
爱喝水的鱼丶1 天前
SAP-ABAP:SELECT大数据量查询性能调优——避免嵌套循环、减少数据库交互的核心方案
开发语言·数据库·sql·性能优化·sap·abap·erp
Revolution611 天前
首屏变慢后,应该先查资源下载、脚本执行还是接口请求
前端·性能优化·前端工程化
做前端的娜娜子1 天前
React 性能优化
掘金·金石计划
云栖梦泽在2 天前
稳如狗网络工具箱:IP、DNS、WebRTC、测速、连通性和全球延迟检测工具简明介绍
网络·网络协议·tcp/ip·网络安全·性能优化·webrtc
井川廊咏2 天前
初探性能优化——2个月到4小时的性能提升
jvm·数据库·性能优化